From 6bfc5b3a3cc263dbd3b804d2123283e0b7629b7c Mon Sep 17 00:00:00 2001 From: dimgigov Date: Thu, 21 May 2026 15:49:11 +0300 Subject: [PATCH] feat(clients): add nim-allographer client with BaraDB driver support - Pure Nim wire protocol client (async + sync, no C FFI) - Query Builder: fluent API, SQL generator, execution, transactions - Schema Builder: create/alter/drop tables, column operations - Connection pool with aging and timeout handling - ORM integration via compile-time DB_BARADB switch - Tests: test_open, test_query - Documentation: README.md, PLAN_BARADB.md --- .../nim-allographer/.cursor/rules/branch.mdc | 31 + ...columnchar-columnstring-and-columnuuid.mdc | 130 + ...rapherpostgresql-非同期待機改善-設計書.mdc | 156 + .../rules/branch/321-mariadb-nonblocking.mdc | 240 + .../323-database-url-for-connection.mdc | 174 + .../rules/branch/333-prepared-statement.mdc | 167 + .../branch/336-update-prepared-statement.mdc | 206 + .../rules/branch/340-aged-connection.mdc | 271 + .../.cursor/rules/branch/fix.mdc | 35 + .../nim-allographer/.cursor/rules/project.mdc | 201 + clients/nim-allographer/.env | 17 + .../nim-allographer/.github/pr-labeler.yml | 3 + .../.github/release-drafter.yml | 19 + .../.github/workflows/docs.yml.bk | 33 + .../nim-allographer/.github/workflows/nim.yml | 61 + .../.github/workflows/pr-labeler.yml | 15 + clients/nim-allographer/.gitignore | 32 + clients/nim-allographer/AGENTS.md | 22 + clients/nim-allographer/LICENSE | 20 + clients/nim-allographer/PLAN_BARADB.md | 353 + clients/nim-allographer/README.md | 204 + clients/nim-allographer/allographer.nimble | 68 + clients/nim-allographer/compose.test.yaml | 50 + clients/nim-allographer/compose.yaml | 71 + clients/nim-allographer/config.nims | 7 + .../nim-allographer/docker/adminer/Dockerfile | 3 + .../docker/adminer/login-password-less.php | 9 + .../nim-allographer/docker/alpine/Dockerfile | 28 + .../nim-allographer/docker/ubuntu/Dockerfile | 48 + .../docker/ubuntu/test.Dockerfile | 40 + clients/nim-allographer/docs/connection.html | 93 + clients/nim-allographer/docs/connection.idx | 1 + clients/nim-allographer/docs/dochack.js | 1607 ++++ clients/nim-allographer/docs/env.html | 134 + clients/nim-allographer/docs/env.idx | 6 + clients/nim-allographer/docs/nimdoc.out.css | 1026 +++ .../nim-allographer/docs/query_builder.html | 93 + .../nim-allographer/docs/query_builder.idx | 1 + .../nim-allographer/docs/schema_builder.html | 93 + .../nim-allographer/docs/schema_builder.idx | 1 + clients/nim-allographer/docs/theindex.html | 7804 +++++++++++++++++ clients/nim-allographer/docs/utils.html | 104 + clients/nim-allographer/docs/utils.idx | 2 + .../nim-allographer/docs/v2/connection.html | 94 + .../nim-allographer/docs/v2/connection.idx | 1 + clients/nim-allographer/docs/v2/dochack.js | 1728 ++++ clients/nim-allographer/docs/v2/env.html | 135 + clients/nim-allographer/docs/v2/env.idx | 6 + .../nim-allographer/docs/v2/nimdoc.out.css | 1049 +++ .../docs/v2/query_builder.html | 94 + .../nim-allographer/docs/v2/query_builder.idx | 1 + .../docs/v2/query_builder/enums.html | 112 + .../docs/v2/query_builder/enums.idx | 8 + .../docs/v2/query_builder/error.html | 129 + .../docs/v2/query_builder/error.idx | 3 + .../v2/query_builder/libs/database_url.html | 274 + .../v2/query_builder/libs/database_url.idx | 13 + .../libs/mariadb/mariadb_impl.html | 295 + .../libs/mariadb/mariadb_impl.idx | 15 + .../libs/mariadb/mariadb_lib.html | 207 + .../libs/mariadb/mariadb_lib.idx | 9 + .../libs/mariadb/mariadb_rdb.html | 1801 ++++ .../libs/mariadb/mariadb_rdb.idx | 156 + .../query_builder/libs/mysql/mysql_impl.html | 294 + .../query_builder/libs/mysql/mysql_impl.idx | 15 + .../query_builder/libs/mysql/mysql_lib.html | 207 + .../v2/query_builder/libs/mysql/mysql_lib.idx | 9 + .../query_builder/libs/mysql/mysql_rdb.html | 1453 +++ .../v2/query_builder/libs/mysql/mysql_rdb.idx | 133 + .../libs/postgres/postgres_impl.html | 323 + .../libs/postgres/postgres_impl.idx | 15 + .../libs/postgres/postgres_lib.html | 314 + .../libs/postgres/postgres_lib.idx | 15 + .../libs/postgres/postgres_rdb.html | 2446 ++++++ .../libs/postgres/postgres_rdb.idx | 187 + .../libs/sqlite/sqlite_impl.html | 277 + .../query_builder/libs/sqlite/sqlite_impl.idx | 12 + .../query_builder/libs/sqlite/sqlite_lib.html | 331 + .../query_builder/libs/sqlite/sqlite_lib.idx | 13 + .../query_builder/libs/sqlite/sqlite_rdb.html | 2698 ++++++ .../query_builder/libs/sqlite/sqlite_rdb.idx | 196 + .../libs/surreal/surreal_impl.html | 230 + .../libs/surreal/surreal_impl.idx | 11 + .../libs/surreal/surreal_lib.html | 148 + .../libs/surreal/surreal_lib.idx | 6 + .../libs/surreal/surreal_rdb.html | 105 + .../libs/surreal/surreal_rdb.idx | 2 + .../docs/v2/query_builder/log.html | 168 + .../docs/v2/query_builder/log.idx | 5 + .../query_builder/models/database_types.html | 304 + .../query_builder/models/database_types.idx | 51 + .../models/mariadb/mariadb_exec.html | 674 ++ .../models/mariadb/mariadb_exec.idx | 48 + .../models/mariadb/mariadb_open.html | 147 + .../models/mariadb/mariadb_open.idx | 5 + .../models/mariadb/mariadb_query.html | 471 + .../models/mariadb/mariadb_query.idx | 32 + .../models/mariadb/mariadb_transaction.html | 114 + .../models/mariadb/mariadb_transaction.idx | 2 + .../models/mariadb/mariadb_types.html | 272 + .../models/mariadb/mariadb_types.idx | 10 + .../models/mariadb/query/mariadb_builder.html | 323 + .../models/mariadb/query/mariadb_builder.idx | 15 + .../mariadb/query/mariadb_generator.html | 611 ++ .../mariadb/query/mariadb_generator.idx | 33 + .../models/mysql/mysql_exec.html | 670 ++ .../query_builder/models/mysql/mysql_exec.idx | 48 + .../models/mysql/mysql_open.html | 147 + .../query_builder/models/mysql/mysql_open.idx | 5 + .../models/mysql/mysql_query.html | 469 + .../models/mysql/mysql_query.idx | 32 + .../models/mysql/mysql_transaction.html | 114 + .../models/mysql/mysql_transaction.idx | 2 + .../models/mysql/mysql_types.html | 268 + .../models/mysql/mysql_types.idx | 10 + .../models/mysql/query/mysql_builder.html | 323 + .../models/mysql/query/mysql_builder.idx | 15 + .../models/mysql/query/mysql_generator.html | 610 ++ .../models/mysql/query/mysql_generator.idx | 33 + .../docs/v2/query_builder/models/orm.html | 116 + .../docs/v2/query_builder/models/orm.idx | 4 + .../models/postgres/poatgres_transaction.html | 114 + .../models/postgres/poatgres_transaction.idx | 2 + .../models/postgres/postgres_exec.html | 677 ++ .../models/postgres/postgres_exec.idx | 48 + .../models/postgres/postgres_open.html | 149 + .../models/postgres/postgres_open.idx | 5 + .../models/postgres/postgres_query.html | 475 + .../models/postgres/postgres_query.idx | 32 + .../models/postgres/postgres_types.html | 247 + .../models/postgres/postgres_types.idx | 9 + .../postgres/query/postgres_builder.html | 323 + .../postgres/query/postgres_builder.idx | 15 + .../postgres/query/postgres_generator.html | 611 ++ .../postgres/query/postgres_generator.idx | 33 + .../models/sqlite/query/sqlite_builder.html | 323 + .../models/sqlite/query/sqlite_builder.idx | 15 + .../models/sqlite/query/sqlite_generator.html | 611 ++ .../models/sqlite/query/sqlite_generator.idx | 33 + .../models/sqlite/sqlite_exec.html | 673 ++ .../models/sqlite/sqlite_exec.idx | 48 + .../models/sqlite/sqlite_open.html | 135 + .../models/sqlite/sqlite_open.idx | 4 + .../models/sqlite/sqlite_query.html | 470 + .../models/sqlite/sqlite_query.idx | 32 + .../models/sqlite/sqlite_transaction.html | 114 + .../models/sqlite/sqlite_transaction.idx | 2 + .../models/sqlite/sqlite_types.html | 232 + .../models/sqlite/sqlite_types.idx | 8 + .../models/surreal/query/surreal_builder.html | 291 + .../models/surreal/query/surreal_builder.idx | 13 + .../surreal/query/surreal_generator.html | 597 ++ .../surreal/query/surreal_generator.idx | 32 + .../models/surreal/surreal_exec.html | 579 ++ .../models/surreal/surreal_exec.idx | 37 + .../models/surreal/surreal_open.html | 126 + .../models/surreal/surreal_open.idx | 2 + .../models/surreal/surreal_query.html | 458 + .../models/surreal/surreal_query.idx | 29 + .../models/surreal/surreal_types.html | 317 + .../models/surreal/surreal_types.idx | 18 + .../docs/v2/schema_builder.html | 94 + .../docs/v2/schema_builder.idx | 1 + .../docs/v2/schema_builder/enums.html | 176 + .../docs/v2/schema_builder/enums.idx | 51 + .../docs/v2/schema_builder/models/column.html | 855 ++ .../docs/v2/schema_builder/models/column.idx | 52 + .../docs/v2/schema_builder/models/table.html | 223 + .../docs/v2/schema_builder/models/table.idx | 9 + .../queries/mariadb/add_column.html | 117 + .../queries/mariadb/add_column.idx | 2 + .../queries/mariadb/change_column.html | 117 + .../queries/mariadb/change_column.idx | 2 + .../mariadb/create_migration_table.html | 117 + .../mariadb/create_migration_table.idx | 2 + .../queries/mariadb/create_table.html | 117 + .../queries/mariadb/create_table.idx | 2 + .../queries/mariadb/drop_column.html | 117 + .../queries/mariadb/drop_column.idx | 2 + .../queries/mariadb/drop_table.html | 117 + .../queries/mariadb/drop_table.idx | 2 + .../queries/mariadb/mariadb_query_type.html | 153 + .../queries/mariadb/mariadb_query_type.idx | 5 + .../queries/mariadb/rename_column.html | 117 + .../queries/mariadb/rename_column.idx | 2 + .../queries/mariadb/rename_table.html | 117 + .../queries/mariadb/rename_table.idx | 2 + .../queries/mariadb/reset_table.html | 135 + .../queries/mariadb/reset_table.idx | 3 + .../queries/mariadb/schema_utils.html | 202 + .../queries/mariadb/schema_utils.idx | 8 + .../queries/mariadb/sub/add_column_query.html | 115 + .../queries/mariadb/sub/add_column_query.idx | 2 + .../mariadb/sub/change_column_query.html | 147 + .../mariadb/sub/change_column_query.idx | 4 + .../mariadb/sub/create_column_query.html | 163 + .../mariadb/sub/create_column_query.idx | 5 + .../queries/mysql/add_column.html | 117 + .../queries/mysql/add_column.idx | 2 + .../queries/mysql/change_column.html | 118 + .../queries/mysql/change_column.idx | 2 + .../queries/mysql/create_migration_table.html | 117 + .../queries/mysql/create_migration_table.idx | 2 + .../queries/mysql/create_table.html | 117 + .../queries/mysql/create_table.idx | 2 + .../queries/mysql/drop_column.html | 117 + .../queries/mysql/drop_column.idx | 2 + .../queries/mysql/drop_table.html | 117 + .../queries/mysql/drop_table.idx | 2 + .../queries/mysql/mysql_query_type.html | 153 + .../queries/mysql/mysql_query_type.idx | 5 + .../queries/mysql/rename_column.html | 117 + .../queries/mysql/rename_column.idx | 2 + .../queries/mysql/rename_table.html | 117 + .../queries/mysql/rename_table.idx | 2 + .../queries/mysql/reset_table.html | 135 + .../queries/mysql/reset_table.idx | 3 + .../queries/mysql/schema_utils.html | 202 + .../queries/mysql/schema_utils.idx | 8 + .../queries/mysql/sub/add_column_query.html | 115 + .../queries/mysql/sub/add_column_query.idx | 2 + .../mysql/sub/change_column_query.html | 147 + .../queries/mysql/sub/change_column_query.idx | 4 + .../mysql/sub/create_column_query.html | 163 + .../queries/mysql/sub/create_column_query.idx | 5 + .../queries/mysql/sub/is_exists.html | 137 + .../queries/mysql/sub/is_exists.idx | 3 + .../queries/postgres/add_column.html | 117 + .../queries/postgres/add_column.idx | 2 + .../queries/postgres/change_column.html | 117 + .../queries/postgres/change_column.idx | 2 + .../postgres/create_migration_table.html | 117 + .../postgres/create_migration_table.idx | 2 + .../queries/postgres/create_table.html | 117 + .../queries/postgres/create_table.idx | 2 + .../queries/postgres/drop_column.html | 117 + .../queries/postgres/drop_column.idx | 2 + .../queries/postgres/drop_table.html | 117 + .../queries/postgres/drop_table.idx | 2 + .../queries/postgres/postgres_query_type.html | 155 + .../queries/postgres/postgres_query_type.idx | 5 + .../queries/postgres/rename_column.html | 117 + .../queries/postgres/rename_column.idx | 2 + .../queries/postgres/rename_table.html | 117 + .../queries/postgres/rename_table.idx | 2 + .../queries/postgres/reset_table.html | 135 + .../queries/postgres/reset_table.idx | 3 + .../queries/postgres/schema_utils.html | 204 + .../queries/postgres/schema_utils.idx | 8 + .../postgres/sub/add_column_query.html | 115 + .../queries/postgres/sub/add_column_query.idx | 2 + .../postgres/sub/change_column_query.html | 115 + .../postgres/sub/change_column_query.idx | 2 + .../postgres/sub/create_column_query.html | 179 + .../postgres/sub/create_column_query.idx | 6 + .../queries/sqlite/add_column.html | 117 + .../queries/sqlite/add_column.idx | 2 + .../queries/sqlite/change_column.html | 123 + .../queries/sqlite/change_column.idx | 2 + .../sqlite/create_migration_table.html | 117 + .../queries/sqlite/create_migration_table.idx | 2 + .../queries/sqlite/create_table.html | 117 + .../queries/sqlite/create_table.idx | 2 + .../queries/sqlite/drop_column.html | 122 + .../queries/sqlite/drop_column.idx | 2 + .../queries/sqlite/drop_table.html | 117 + .../queries/sqlite/drop_table.idx | 2 + .../queries/sqlite/rename_column.html | 117 + .../queries/sqlite/rename_column.idx | 2 + .../queries/sqlite/rename_table.html | 117 + .../queries/sqlite/rename_table.idx | 2 + .../queries/sqlite/reset_table.html | 135 + .../queries/sqlite/reset_table.idx | 3 + .../queries/sqlite/schema_utils.html | 219 + .../queries/sqlite/schema_utils.idx | 9 + .../queries/sqlite/sqlite_query_type.html | 153 + .../queries/sqlite/sqlite_query_type.idx | 5 + .../queries/sqlite/sub/add_column_query.html | 120 + .../queries/sqlite/sub/add_column_query.idx | 2 + .../sqlite/sub/create_column_query.html | 147 + .../sqlite/sub/create_column_query.idx | 4 + .../queries/surreal/add_column.html | 117 + .../queries/surreal/add_column.idx | 2 + .../queries/surreal/change_column.html | 117 + .../queries/surreal/change_column.idx | 2 + .../surreal/create_migration_table.html | 117 + .../surreal/create_migration_table.idx | 2 + .../surreal/create_sequence_table.html | 136 + .../queries/surreal/create_sequence_table.idx | 3 + .../queries/surreal/create_table.html | 118 + .../queries/surreal/create_table.idx | 2 + .../queries/surreal/drop_column.html | 118 + .../queries/surreal/drop_column.idx | 2 + .../queries/surreal/drop_table.html | 118 + .../queries/surreal/drop_table.idx | 2 + .../queries/surreal/rename_column.html | 117 + .../queries/surreal/rename_column.idx | 2 + .../queries/surreal/rename_table.html | 117 + .../queries/surreal/rename_table.idx | 2 + .../queries/surreal/reset_table.html | 134 + .../queries/surreal/reset_table.idx | 3 + .../queries/surreal/schema_utils.html | 203 + .../queries/surreal/schema_utils.idx | 8 + .../surreal/sub/create_column_query.html | 115 + .../surreal/sub/create_column_query.idx | 2 + .../queries/surreal/surreal_query_type.html | 153 + .../queries/surreal/surreal_query_type.idx | 5 + .../usecases/mariadb/alter.html | 117 + .../schema_builder/usecases/mariadb/alter.idx | 2 + .../usecases/mariadb/create.html | 117 + .../usecases/mariadb/create.idx | 2 + .../usecases/mariadb/create_query_def.html | 125 + .../usecases/mariadb/create_query_def.idx | 4 + .../usecases/mariadb/create_schema.html | 119 + .../usecases/mariadb/create_schema.idx | 2 + .../schema_builder/usecases/mariadb/drop.html | 117 + .../schema_builder/usecases/mariadb/drop.idx | 2 + .../schema_builder/usecases/mysql/alter.html | 118 + .../schema_builder/usecases/mysql/alter.idx | 2 + .../schema_builder/usecases/mysql/create.html | 118 + .../schema_builder/usecases/mysql/create.idx | 2 + .../usecases/mysql/create_query_def.html | 125 + .../usecases/mysql/create_query_def.idx | 4 + .../usecases/mysql/create_schema.html | 119 + .../usecases/mysql/create_schema.idx | 2 + .../schema_builder/usecases/mysql/drop.html | 118 + .../v2/schema_builder/usecases/mysql/drop.idx | 2 + .../usecases/postgres/alter.html | 117 + .../usecases/postgres/alter.idx | 2 + .../usecases/postgres/create.html | 117 + .../usecases/postgres/create.idx | 2 + .../usecases/postgres/create_query_def.html | 125 + .../usecases/postgres/create_query_def.idx | 4 + .../usecases/postgres/create_schema.html | 119 + .../usecases/postgres/create_schema.idx | 2 + .../usecases/postgres/drop.html | 117 + .../schema_builder/usecases/postgres/drop.idx | 2 + .../schema_builder/usecases/sqlite/alter.html | 118 + .../schema_builder/usecases/sqlite/alter.idx | 2 + .../usecases/sqlite/create.html | 117 + .../schema_builder/usecases/sqlite/create.idx | 2 + .../usecases/sqlite/create_query_def.html | 125 + .../usecases/sqlite/create_query_def.idx | 4 + .../usecases/sqlite/create_schema.html | 119 + .../usecases/sqlite/create_schema.idx | 2 + .../schema_builder/usecases/sqlite/drop.html | 117 + .../schema_builder/usecases/sqlite/drop.idx | 2 + .../usecases/sub/migration_table_def.html | 114 + .../usecases/sub/migration_table_def.idx | 2 + .../usecases/surreal/alter.html | 118 + .../schema_builder/usecases/surreal/alter.idx | 2 + .../usecases/surreal/create.html | 118 + .../usecases/surreal/create.idx | 2 + .../usecases/surreal/create_query_def.html | 125 + .../usecases/surreal/create_query_def.idx | 4 + .../usecases/surreal/create_schema.html | 119 + .../usecases/surreal/create_schema.idx | 2 + .../schema_builder/usecases/surreal/drop.html | 118 + .../schema_builder/usecases/surreal/drop.idx | 2 + clients/nim-allographer/docs/v2/theindex.html | 5569 ++++++++++++ clients/nim-allographer/docs/v2/utils.html | 105 + clients/nim-allographer/docs/v2/utils.idx | 2 + .../docs/v2/utils/snake_to_camel.html | 105 + .../docs/v2/utils/snake_to_camel.idx | 2 + .../documents/rdb/benchmark_result.md | 35 + .../rdb/postgres_efficiency_report.md | 288 + .../documents/rdb/query_builder.md | 773 ++ .../documents/rdb/schema_builder.md | 248 + .../rdb/surreal_efficiency_report.md | 222 + .../documents/rdb/typeDifinition.md | 60 + .../documents/surrealdb/prepared_statement.md | 77 + .../documents/surrealdb/query_builder.md | 547 ++ .../documents/surrealdb/schema_builder.md | 147 + clients/nim-allographer/example/.env | 22 + .../nim-allographer/example/columnType.nim | 58 + .../nim-allographer/example/connections.nim | 21 + clients/nim-allographer/example/database/.env | 12 + .../example/database/connection.nim | 17 + .../example/database/develop.sh | 5 + .../migrations/create_all_type_table.nim | 42 + .../database/migrations/init_databaase.nim | 23 + .../example/database/migrations/migrate.nim | 12 + .../example/database/production.sh | 5 + .../example/database/schema.nim | 67 + .../database/seeder/data/post_seeder.nim | 32 + .../database/seeder/data/user_seeder.nim | 24 + .../example/database/seeder/develop.nim | 16 + .../example/database/seeder/production.nim | 14 + .../example/database/seeder/staging.nim | 14 + .../example/database/staging.sh | 5 + .../example/ddd/repository.nim | 17 + .../nim-allographer/example/ddd/service.nim | 18 + .../example/mariadb/benchmark.nim | 126 + .../nim-allographer/example/mariadb/test.c | 164 + clients/nim-allographer/example/migrate.nim | 41 + clients/nim-allographer/example/mysql/test.c | 164 + .../example/postgres/benchmark.nim | 123 + .../nim-allographer/example/postgres/test.c | 147 + clients/nim-allographer/example/prepare.nim | 33 + .../nim-allographer/example/rearchtect.nim | 48 + clients/nim-allographer/example/run.nim | 131 + .../nim-allographer/example/sarrogate_key.nim | 55 + clients/nim-allographer/example/setup.nim | 51 + .../example/sqlite/benchmark.nim | 126 + clients/nim-allographer/example/surreal.nim | 209 + .../example/surreal/autoincremets.nim | 62 + .../nim-allographer/example/surreal/test1.nim | 49 + clients/nim-allographer/example/surrealDb.nim | 17 + .../nim-allographer/example/transaction.nim | 56 + .../nim-allographer/example/typedReturn.nim | 35 + clients/nim-allographer/example/upperCase.nim | 10 + clients/nim-allographer/makefile | 9 + clients/nim-allographer/permission.sh | 8 + .../src/allographer/connection.nim | 28 + .../nim-allographer/src/allographer/env.nim | 27 + .../src/allographer/query_builder.nim | 41 + .../src/allographer/query_builder.out | Bin 0 -> 328792 bytes .../src/allographer/query_builder/enums.nim | 8 + .../src/allographer/query_builder/error.nim | 9 + .../libs/baradb/baradb_client.nim | 706 ++ .../query_builder/libs/database_url.nim | 101 + .../libs/mariadb/mariadb_impl.nim | 558 ++ .../libs/mariadb/mariadb_lib.nim | 232 + .../libs/mariadb/mariadb_rdb.nim | 1220 +++ .../libs/mariadb/mariadb_rdb.nim.bk | 1158 +++ .../query_builder/libs/mysql/mysql_impl.nim | 445 + .../query_builder/libs/mysql/mysql_lib.nim | 224 + .../query_builder/libs/mysql/mysql_rdb.nim | 1154 +++ .../query_builder/libs/mysql/mysql_rdb.nim.bk | 1157 +++ .../libs/postgres/postgres_impl.nim | 431 + .../libs/postgres/postgres_lib.nim | 364 + .../libs/postgres/postgres_rdb.nim | 374 + .../query_builder/libs/sqlite/sqlite_impl.nim | 263 + .../query_builder/libs/sqlite/sqlite_lib.nim | 200 + .../query_builder/libs/sqlite/sqlite_rdb.nim | 384 + .../libs/surreal/surreal_impl.nim | 148 + .../libs/surreal/surreal_lib.nim | 197 + .../libs/surreal/surreal_rdb.nim | 6 + .../src/allographer/query_builder/log.nim | 58 + .../models/baradb/baradb_exec.nim | 716 ++ .../models/baradb/baradb_open.nim | 52 + .../models/baradb/baradb_query.nim | 386 + .../models/baradb/baradb_transaction.nim | 42 + .../models/baradb/baradb_types.nim | 90 + .../models/baradb/query/baradb_builder.nim | 237 + .../models/baradb/query/baradb_generator.nim | 397 + .../query_builder/models/database_types.nim | 71 + .../models/mariadb/mariadb_exec.nim | 1229 +++ .../models/mariadb/mariadb_open.nim | 90 + .../models/mariadb/mariadb_query.nim | 439 + .../models/mariadb/mariadb_transaction.nim | 42 + .../models/mariadb/mariadb_types.nim | 115 + .../models/mariadb/query/mariadb_builder.nim | 228 + .../mariadb/query/mariadb_generator.nim | 429 + .../query_builder/models/mysql/mysql_exec.nim | 1196 +++ .../query_builder/models/mysql/mysql_open.nim | 89 + .../models/mysql/mysql_query.nim | 438 + .../models/mysql/mysql_transaction.nim | 42 + .../models/mysql/mysql_types.nim | 109 + .../models/mysql/query/mysql_builder.nim | 228 + .../models/mysql/query/mysql_generator.nim | 430 + .../allographer/query_builder/models/orm.nim | 18 + .../models/postgres/poatgres_transaction.nim | 42 + .../models/postgres/postgres_exec.nim | 1330 +++ .../models/postgres/postgres_open.nim | 82 + .../models/postgres/postgres_query.nim | 441 + .../models/postgres/postgres_types.nim | 101 + .../postgres/query/postgres_builder.nim | 228 + .../postgres/query/postgres_generator.nim | 429 + .../models/sqlite/query/sqlite_builder.nim | 229 + .../models/sqlite/query/sqlite_generator.nim | 431 + .../models/sqlite/sqlite_exec.nim | 1336 +++ .../models/sqlite/sqlite_open.nim | 63 + .../models/sqlite/sqlite_query.nim | 411 + .../models/sqlite/sqlite_transaction.nim | 42 + .../models/sqlite/sqlite_types.nim | 93 + .../models/surreal/query/surreal_builder.nim | 197 + .../surreal/query/surreal_generator.nim | 391 + .../models/surreal/surreal_exec.nim | 982 +++ .../models/surreal/surreal_open.nim | 85 + .../models/surreal/surreal_query.nim | 452 + .../models/surreal/surreal_types.nim | 135 + .../query_builder/prepared_param.nim | 115 + .../src/allographer/query_builder/readme.md | 28 + .../src/allographer/schema_builder.nim | 48 + .../src/allographer/schema_builder.out | Bin 0 -> 373136 bytes .../src/allographer/schema_builder/enums.nim | 62 + .../schema_builder/models/column.nim | 421 + .../schema_builder/models/table.nim | 72 + .../queries/baradb/add_column.nim | 8 + .../queries/baradb/baradb_query_type.nim | 14 + .../queries/baradb/change_column.nim | 8 + .../queries/baradb/create_migration_table.nim | 12 + .../queries/baradb/create_table.nim | 16 + .../queries/baradb/drop_column.nim | 8 + .../queries/baradb/drop_table.nim | 7 + .../queries/baradb/rename_column.nim | 8 + .../queries/baradb/rename_table.nim | 7 + .../queries/baradb/reset_table.nim | 7 + .../queries/baradb/schema_utils.nim | 12 + .../queries/baradb/sub/add_column_query.nim | 8 + .../baradb/sub/change_column_query.nim | 6 + .../baradb/sub/create_column_query.nim | 24 + .../queries/mariadb/add_column.nim | 21 + .../queries/mariadb/change_column.nim | 31 + .../mariadb/create_migration_table.nim | 38 + .../queries/mariadb/create_table.nim | 64 + .../queries/mariadb/drop_column.nim | 19 + .../queries/mariadb/drop_table.nim | 18 + .../queries/mariadb/mariadb_query_type.nim | 15 + .../queries/mariadb/rename_column.nim | 19 + .../queries/mariadb/rename_table.nim | 19 + .../queries/mariadb/reset_table.nim | 13 + .../queries/mariadb/schema_utils.nim | 109 + .../queries/mariadb/sub/add_column_query.nim | 529 ++ .../mariadb/sub/change_column_query.nim | 521 ++ .../mariadb/sub/create_column_query.nim | 600 ++ .../queries/mysql/add_column.nim | 21 + .../queries/mysql/change_column.nim | 35 + .../queries/mysql/create_migration_table.nim | 52 + .../queries/mysql/create_table.nim | 78 + .../queries/mysql/drop_column.nim | 19 + .../queries/mysql/drop_table.nim | 18 + .../queries/mysql/mysql_query_type.nim | 15 + .../queries/mysql/rename_column.nim | 19 + .../queries/mysql/rename_table.nim | 19 + .../queries/mysql/reset_table.nim | 13 + .../queries/mysql/schema_utils.nim | 109 + .../queries/mysql/sub/add_column_query.nim | 546 ++ .../queries/mysql/sub/change_column_query.nim | 536 ++ .../queries/mysql/sub/create_column_query.nim | 610 ++ .../queries/mysql/sub/is_exists.nim | 34 + .../queries/postgres/add_column.nim | 21 + .../queries/postgres/change_column.nim | 21 + .../postgres/create_migration_table.nim | 39 + .../queries/postgres/create_table.nim | 78 + .../queries/postgres/drop_column.nim | 19 + .../queries/postgres/drop_table.nim | 18 + .../queries/postgres/postgres_query_type.nim | 15 + .../queries/postgres/rename_column.nim | 19 + .../queries/postgres/rename_table.nim | 19 + .../queries/postgres/reset_table.nim | 13 + .../queries/postgres/schema_utils.nim | 109 + .../queries/postgres/sub/add_column_query.nim | 508 ++ .../postgres/sub/change_column_query.nim | 429 + .../postgres/sub/create_column_query.nim | 466 + .../queries/sqlite/add_column.nim | 50 + .../queries/sqlite/change_column.nim | 86 + .../queries/sqlite/create_migration_table.nim | 38 + .../queries/sqlite/create_table.nim | 59 + .../queries/sqlite/drop_column.nim | 57 + .../queries/sqlite/drop_table.nim | 19 + .../queries/sqlite/rename_column.nim | 20 + .../queries/sqlite/rename_table.nim | 19 + .../queries/sqlite/reset_table.nim | 13 + .../queries/sqlite/schema_utils.nim | 116 + .../queries/sqlite/sqlite_query_type.nim | 14 + .../queries/sqlite/sub/add_column_query.nim | 437 + .../sqlite/sub/create_column_query.nim | 409 + .../queries/surreal/add_column.nim | 21 + .../queries/surreal/change_column.nim | 33 + .../surreal/create_migration_table.nim | 39 + .../queries/surreal/create_sequence_table.nim | 32 + .../queries/surreal/create_table.nim | 29 + .../queries/surreal/drop_column.nim | 19 + .../queries/surreal/drop_table.nim | 18 + .../queries/surreal/rename_column.nim | 26 + .../queries/surreal/rename_table.nim | 27 + .../queries/surreal/reset_table.nim | 13 + .../queries/surreal/schema_utils.nim | 129 + .../surreal/sub/create_column_query.nim | 606 ++ .../queries/surreal/surreal_query_type.nim | 15 + .../schema_builder/usecases/baradb/alter.nim | 45 + .../schema_builder/usecases/baradb/create.nim | 24 + .../usecases/baradb/create_query_def.nim | 10 + .../usecases/baradb/create_schema.nim | 60 + .../schema_builder/usecases/baradb/drop.nim | 18 + .../schema_builder/usecases/mariadb/alter.nim | 49 + .../usecases/mariadb/create.nim | 31 + .../usecases/mariadb/create_query_def.nim | 11 + .../usecases/mariadb/create_schema.nim | 98 + .../schema_builder/usecases/mariadb/drop.nim | 22 + .../schema_builder/usecases/mysql/alter.nim | 53 + .../schema_builder/usecases/mysql/create.nim | 31 + .../usecases/mysql/create_query_def.nim | 11 + .../usecases/mysql/create_schema.nim | 98 + .../schema_builder/usecases/mysql/drop.nim | 22 + .../usecases/postgres/alter.nim | 53 + .../usecases/postgres/create.nim | 31 + .../usecases/postgres/create_query_def.nim | 11 + .../usecases/postgres/create_schema.nim | 100 + .../schema_builder/usecases/postgres/drop.nim | 22 + .../schema_builder/usecases/sqlite/alter.nim | 49 + .../schema_builder/usecases/sqlite/create.nim | 31 + .../usecases/sqlite/create_query_def.nim | 11 + .../usecases/sqlite/create_schema.nim | 96 + .../schema_builder/usecases/sqlite/drop.nim | 22 + .../usecases/sub/migration_table_def.nim | 11 + .../schema_builder/usecases/surreal/alter.nim | 49 + .../usecases/surreal/create.nim | 35 + .../usecases/surreal/create_query_def.nim | 11 + .../usecases/surreal/create_schema.nim | 102 + .../schema_builder/usecases/surreal/drop.nim | 22 + .../nim-allographer/src/allographer/utils.nim | 13 + .../src/allographer/utils/snake_to_camel.nim | 7 + .../nim-allographer/tests/baradb/config.nims | 2 + .../tests/baradb/connections.nim | 6 + .../tests/baradb/test_open.nim | 13 + .../tests/baradb/test_query.nim | 82 + .../nim-allographer/tests/clear_tables.nim | 71 + clients/nim-allographer/tests/config.nims | 8 + .../tests/mariadb/connections.nim | 11 + .../tests/mariadb/test_create_schema.nim | 108 + .../tests/mariadb/test_open.nim | 45 + .../tests/mariadb/test_pool_wait.nim | 29 + .../tests/mariadb/test_prepared_statement.nim | 149 + .../tests/mariadb/test_query.nim | 563 ++ .../tests/mariadb/test_schema.nim | 308 + .../tests/mariadb/test_transaction.nim | 128 + .../tests/mysql/connections.nim | 10 + .../tests/mysql/test_create_schema.nim | 108 + .../nim-allographer/tests/mysql/test_open.nim | 45 + .../tests/mysql/test_prepared_statement.nim | 149 + .../tests/mysql/test_query.nim | 565 ++ .../tests/mysql/test_schema.nim | 617 ++ .../tests/mysql/test_transaction.nim | 128 + .../tests/postgres/connections.nim | 10 + .../tests/postgres/test_create_schema.nim | 107 + .../tests/postgres/test_open.nim | 45 + .../tests/postgres/test_pool_wait.nim | 25 + .../postgres/test_prepared_statement.nim | 149 + .../tests/postgres/test_query.nim | 561 ++ .../tests/postgres/test_schema.nim | 332 + .../tests/postgres/test_transaction.nim | 131 + .../tests/sqlite/connections.nim | 11 + .../tests/sqlite/test_create_schema.nim | 107 + .../tests/sqlite/test_open.nim | 21 + .../tests/sqlite/test_pool_wait.nim | 25 + .../tests/sqlite/test_prepared_statement.nim | 149 + .../tests/sqlite/test_query.nim | 568 ++ .../tests/sqlite/test_schema.nim | 298 + .../tests/sqlite/test_schema_length_check.nim | 88 + .../tests/sqlite/test_transaction.nim | 128 + .../tests/surrealdb/connection.nim | 27 + .../tests/surrealdb/connections.nim | 16 + .../tests/surrealdb/test_autoincrements.nim | 97 + .../tests/surrealdb/test_create_schema.nim | 106 + .../tests/surrealdb/test_lib.nim | 26 + .../tests/surrealdb/test_pool_wait.nim | 25 + .../surrealdb/test_prepared_statement.nim | 98 + .../tests/surrealdb/test_query.nim | 507 ++ .../tests/surrealdb/test_schema.nim | 532 ++ .../tests/utils/test_database_url.nim | 42 + .../tests/utils/test_utils.nim | 16 + 654 files changed, 109419 insertions(+) create mode 100644 clients/nim-allographer/.cursor/rules/branch.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/318-bug-incorrect-sqlite-check-constraint-for-columnchar-columnstring-and-columnuuid.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/320-allographerpostgresql-非同期待機改善-設計書.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/321-mariadb-nonblocking.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/323-database-url-for-connection.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/333-prepared-statement.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/336-update-prepared-statement.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/340-aged-connection.mdc create mode 100644 clients/nim-allographer/.cursor/rules/branch/fix.mdc create mode 100644 clients/nim-allographer/.cursor/rules/project.mdc create mode 100644 clients/nim-allographer/.env create mode 100644 clients/nim-allographer/.github/pr-labeler.yml create mode 100644 clients/nim-allographer/.github/release-drafter.yml create mode 100644 clients/nim-allographer/.github/workflows/docs.yml.bk create mode 100644 clients/nim-allographer/.github/workflows/nim.yml create mode 100644 clients/nim-allographer/.github/workflows/pr-labeler.yml create mode 100644 clients/nim-allographer/.gitignore create mode 100644 clients/nim-allographer/AGENTS.md create mode 100644 clients/nim-allographer/LICENSE create mode 100644 clients/nim-allographer/PLAN_BARADB.md create mode 100644 clients/nim-allographer/README.md create mode 100644 clients/nim-allographer/allographer.nimble create mode 100644 clients/nim-allographer/compose.test.yaml create mode 100644 clients/nim-allographer/compose.yaml create mode 100644 clients/nim-allographer/config.nims create mode 100644 clients/nim-allographer/docker/adminer/Dockerfile create mode 100644 clients/nim-allographer/docker/adminer/login-password-less.php create mode 100644 clients/nim-allographer/docker/alpine/Dockerfile create mode 100644 clients/nim-allographer/docker/ubuntu/Dockerfile create mode 100644 clients/nim-allographer/docker/ubuntu/test.Dockerfile create mode 100644 clients/nim-allographer/docs/connection.html create mode 100644 clients/nim-allographer/docs/connection.idx create mode 100644 clients/nim-allographer/docs/dochack.js create mode 100644 clients/nim-allographer/docs/env.html create mode 100644 clients/nim-allographer/docs/env.idx create mode 100644 clients/nim-allographer/docs/nimdoc.out.css create mode 100644 clients/nim-allographer/docs/query_builder.html create mode 100644 clients/nim-allographer/docs/query_builder.idx create mode 100644 clients/nim-allographer/docs/schema_builder.html create mode 100644 clients/nim-allographer/docs/schema_builder.idx create mode 100644 clients/nim-allographer/docs/theindex.html create mode 100644 clients/nim-allographer/docs/utils.html create mode 100644 clients/nim-allographer/docs/utils.idx create mode 100644 clients/nim-allographer/docs/v2/connection.html create mode 100644 clients/nim-allographer/docs/v2/connection.idx create mode 100644 clients/nim-allographer/docs/v2/dochack.js create mode 100644 clients/nim-allographer/docs/v2/env.html create mode 100644 clients/nim-allographer/docs/v2/env.idx create mode 100644 clients/nim-allographer/docs/v2/nimdoc.out.css create mode 100644 clients/nim-allographer/docs/v2/query_builder.html create mode 100644 clients/nim-allographer/docs/v2/query_builder.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/enums.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/enums.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/error.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/error.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/database_url.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/database_url.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/log.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/log.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/database_types.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/database_types.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/orm.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/orm.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.idx create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.html create mode 100644 clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/enums.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/enums.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/models/column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/models/column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/models/table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/models/table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.idx create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.html create mode 100644 clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.idx create mode 100644 clients/nim-allographer/docs/v2/theindex.html create mode 100644 clients/nim-allographer/docs/v2/utils.html create mode 100644 clients/nim-allographer/docs/v2/utils.idx create mode 100644 clients/nim-allographer/docs/v2/utils/snake_to_camel.html create mode 100644 clients/nim-allographer/docs/v2/utils/snake_to_camel.idx create mode 100644 clients/nim-allographer/documents/rdb/benchmark_result.md create mode 100644 clients/nim-allographer/documents/rdb/postgres_efficiency_report.md create mode 100644 clients/nim-allographer/documents/rdb/query_builder.md create mode 100644 clients/nim-allographer/documents/rdb/schema_builder.md create mode 100644 clients/nim-allographer/documents/rdb/surreal_efficiency_report.md create mode 100644 clients/nim-allographer/documents/rdb/typeDifinition.md create mode 100644 clients/nim-allographer/documents/surrealdb/prepared_statement.md create mode 100644 clients/nim-allographer/documents/surrealdb/query_builder.md create mode 100644 clients/nim-allographer/documents/surrealdb/schema_builder.md create mode 100644 clients/nim-allographer/example/.env create mode 100644 clients/nim-allographer/example/columnType.nim create mode 100644 clients/nim-allographer/example/connections.nim create mode 100644 clients/nim-allographer/example/database/.env create mode 100644 clients/nim-allographer/example/database/connection.nim create mode 100755 clients/nim-allographer/example/database/develop.sh create mode 100644 clients/nim-allographer/example/database/migrations/create_all_type_table.nim create mode 100644 clients/nim-allographer/example/database/migrations/init_databaase.nim create mode 100644 clients/nim-allographer/example/database/migrations/migrate.nim create mode 100755 clients/nim-allographer/example/database/production.sh create mode 100644 clients/nim-allographer/example/database/schema.nim create mode 100644 clients/nim-allographer/example/database/seeder/data/post_seeder.nim create mode 100644 clients/nim-allographer/example/database/seeder/data/user_seeder.nim create mode 100644 clients/nim-allographer/example/database/seeder/develop.nim create mode 100644 clients/nim-allographer/example/database/seeder/production.nim create mode 100644 clients/nim-allographer/example/database/seeder/staging.nim create mode 100755 clients/nim-allographer/example/database/staging.sh create mode 100644 clients/nim-allographer/example/ddd/repository.nim create mode 100644 clients/nim-allographer/example/ddd/service.nim create mode 100644 clients/nim-allographer/example/mariadb/benchmark.nim create mode 100644 clients/nim-allographer/example/mariadb/test.c create mode 100644 clients/nim-allographer/example/migrate.nim create mode 100644 clients/nim-allographer/example/mysql/test.c create mode 100644 clients/nim-allographer/example/postgres/benchmark.nim create mode 100644 clients/nim-allographer/example/postgres/test.c create mode 100644 clients/nim-allographer/example/prepare.nim create mode 100644 clients/nim-allographer/example/rearchtect.nim create mode 100644 clients/nim-allographer/example/run.nim create mode 100644 clients/nim-allographer/example/sarrogate_key.nim create mode 100644 clients/nim-allographer/example/setup.nim create mode 100644 clients/nim-allographer/example/sqlite/benchmark.nim create mode 100644 clients/nim-allographer/example/surreal.nim create mode 100644 clients/nim-allographer/example/surreal/autoincremets.nim create mode 100644 clients/nim-allographer/example/surreal/test1.nim create mode 100644 clients/nim-allographer/example/surrealDb.nim create mode 100644 clients/nim-allographer/example/transaction.nim create mode 100644 clients/nim-allographer/example/typedReturn.nim create mode 100644 clients/nim-allographer/example/upperCase.nim create mode 100644 clients/nim-allographer/makefile create mode 100755 clients/nim-allographer/permission.sh create mode 100644 clients/nim-allographer/src/allographer/connection.nim create mode 100644 clients/nim-allographer/src/allographer/env.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder.nim create mode 100755 clients/nim-allographer/src/allographer/query_builder.out create mode 100644 clients/nim-allographer/src/allographer/query_builder/enums.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/error.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/baradb/baradb_client.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/database_url.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_impl.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_lib.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim.bk create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_impl.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_lib.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_rdb.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_rdb.nim.bk create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_impl.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_lib.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_rdb.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_impl.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_lib.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_rdb.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_impl.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_lib.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_rdb.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/log.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_exec.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_open.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_query.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_transaction.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_types.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_builder.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_generator.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/database_types.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_exec.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_open.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_query.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_transaction.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_types.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_builder.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_generator.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_exec.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_open.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_query.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_transaction.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_types.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_builder.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_generator.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/orm.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/postgres/poatgres_transaction.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_exec.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_open.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_query.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_types.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_builder.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_generator.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_builder.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_generator.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_exec.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_open.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_query.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_transaction.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_types.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_builder.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_generator.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_exec.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_open.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_query.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_types.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/prepared_param.nim create mode 100644 clients/nim-allographer/src/allographer/query_builder/readme.md create mode 100644 clients/nim-allographer/src/allographer/schema_builder.nim create mode 100755 clients/nim-allographer/src/allographer/schema_builder.out create mode 100644 clients/nim-allographer/src/allographer/schema_builder/enums.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/models/column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/models/table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/add_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/baradb_query_type.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/change_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_migration_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/reset_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/schema_utils.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/add_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/change_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/create_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/add_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/change_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_migration_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/mariadb_query_type.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/reset_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/schema_utils.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/add_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/change_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/create_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/add_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/change_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_migration_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/mysql_query_type.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/reset_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/schema_utils.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/add_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/change_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/create_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/is_exists.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/add_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/change_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_migration_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/postgres_query_type.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/reset_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/schema_utils.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/add_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/change_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/create_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/add_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/change_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_migration_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/reset_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/schema_utils.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sqlite_query_type.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/add_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/create_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/add_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/change_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_migration_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_sequence_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_column.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/reset_table.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/schema_utils.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/sub/create_column_query.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/queries/surreal/surreal_query_type.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/alter.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_query_def.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_schema.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/drop.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/alter.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_query_def.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_schema.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/drop.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/alter.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_query_def.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_schema.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/drop.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/alter.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_query_def.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_schema.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/drop.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/alter.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_query_def.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_schema.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/drop.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/sub/migration_table_def.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/alter.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_query_def.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_schema.nim create mode 100644 clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/drop.nim create mode 100644 clients/nim-allographer/src/allographer/utils.nim create mode 100644 clients/nim-allographer/src/allographer/utils/snake_to_camel.nim create mode 100644 clients/nim-allographer/tests/baradb/config.nims create mode 100644 clients/nim-allographer/tests/baradb/connections.nim create mode 100644 clients/nim-allographer/tests/baradb/test_open.nim create mode 100644 clients/nim-allographer/tests/baradb/test_query.nim create mode 100644 clients/nim-allographer/tests/clear_tables.nim create mode 100644 clients/nim-allographer/tests/config.nims create mode 100644 clients/nim-allographer/tests/mariadb/connections.nim create mode 100644 clients/nim-allographer/tests/mariadb/test_create_schema.nim create mode 100644 clients/nim-allographer/tests/mariadb/test_open.nim create mode 100644 clients/nim-allographer/tests/mariadb/test_pool_wait.nim create mode 100644 clients/nim-allographer/tests/mariadb/test_prepared_statement.nim create mode 100644 clients/nim-allographer/tests/mariadb/test_query.nim create mode 100644 clients/nim-allographer/tests/mariadb/test_schema.nim create mode 100644 clients/nim-allographer/tests/mariadb/test_transaction.nim create mode 100644 clients/nim-allographer/tests/mysql/connections.nim create mode 100644 clients/nim-allographer/tests/mysql/test_create_schema.nim create mode 100644 clients/nim-allographer/tests/mysql/test_open.nim create mode 100644 clients/nim-allographer/tests/mysql/test_prepared_statement.nim create mode 100644 clients/nim-allographer/tests/mysql/test_query.nim create mode 100644 clients/nim-allographer/tests/mysql/test_schema.nim create mode 100644 clients/nim-allographer/tests/mysql/test_transaction.nim create mode 100644 clients/nim-allographer/tests/postgres/connections.nim create mode 100644 clients/nim-allographer/tests/postgres/test_create_schema.nim create mode 100644 clients/nim-allographer/tests/postgres/test_open.nim create mode 100644 clients/nim-allographer/tests/postgres/test_pool_wait.nim create mode 100644 clients/nim-allographer/tests/postgres/test_prepared_statement.nim create mode 100644 clients/nim-allographer/tests/postgres/test_query.nim create mode 100644 clients/nim-allographer/tests/postgres/test_schema.nim create mode 100644 clients/nim-allographer/tests/postgres/test_transaction.nim create mode 100644 clients/nim-allographer/tests/sqlite/connections.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_create_schema.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_open.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_pool_wait.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_prepared_statement.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_query.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_schema.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_schema_length_check.nim create mode 100644 clients/nim-allographer/tests/sqlite/test_transaction.nim create mode 100644 clients/nim-allographer/tests/surrealdb/connection.nim create mode 100644 clients/nim-allographer/tests/surrealdb/connections.nim create mode 100644 clients/nim-allographer/tests/surrealdb/test_autoincrements.nim create mode 100644 clients/nim-allographer/tests/surrealdb/test_create_schema.nim create mode 100644 clients/nim-allographer/tests/surrealdb/test_lib.nim create mode 100644 clients/nim-allographer/tests/surrealdb/test_pool_wait.nim create mode 100644 clients/nim-allographer/tests/surrealdb/test_prepared_statement.nim create mode 100644 clients/nim-allographer/tests/surrealdb/test_query.nim create mode 100644 clients/nim-allographer/tests/surrealdb/test_schema.nim create mode 100644 clients/nim-allographer/tests/utils/test_database_url.nim create mode 100644 clients/nim-allographer/tests/utils/test_utils.nim diff --git a/clients/nim-allographer/.cursor/rules/branch.mdc b/clients/nim-allographer/.cursor/rules/branch.mdc new file mode 100644 index 0000000..0bbe1fa --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch.mdc @@ -0,0 +1,31 @@ +--- +alwaysApply: true +--- +現在のブランチ名と一致するファイル名のファイルが、リポジトリのルートから見て `.cursor/rules/branch/` 配下にあった場合は、それを読み込んでください。 +各ブランチルールは、ブランチで行うべき開発内容、タスクを小さい粒度で分けた進捗、参考資料の一覧を含みます。 +ブランチルールファイルの拡張子は `.mdc` です。 +エージェントは開発の進度に従って進捗を適時記録してください。 +実装する中で `調査結果・設計まとめ` に間違いがあると判明した場合は、その都度、より正しいと考えられる設計方針を書き込んでください。 + +以下はブランチルールの雛形です + +``` +--- +description: {ブランチ名}ブランチでの開発時に読み込む +alwaysApply: false +--- +{ブランチルール名} +=== + +このブランチで実装することは以下の通りです。 +- + +## 進捗 +- [ ] + +## 参考資料 +- + +## 調査結果・設計まとめ +``` + diff --git a/clients/nim-allographer/.cursor/rules/branch/318-bug-incorrect-sqlite-check-constraint-for-columnchar-columnstring-and-columnuuid.mdc b/clients/nim-allographer/.cursor/rules/branch/318-bug-incorrect-sqlite-check-constraint-for-columnchar-columnstring-and-columnuuid.mdc new file mode 100644 index 0000000..5178068 --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/318-bug-incorrect-sqlite-check-constraint-for-columnchar-columnstring-and-columnuuid.mdc @@ -0,0 +1,130 @@ +--- +description: 318-bug-incorrect-sqlite-check-constraint-for-columnchar-columnstring-and-columnuuidブランチでの開発時に読み込む +alwaysApply: false +--- +SQLite CHECK Constraint 引用誤り修正 ブランチルール +=== + +このブランチで実装することは以下の通りです。 +- SQLite の schema builder において、`Column.char`, `Column.string`, `Column.uuid` が生成する `CHECK` 制約の列参照が文字列リテラル扱いになっている不具合を修正する。 +- `CREATE TABLE` と `ALTER TABLE ... ADD COLUMN` の両方で、`length('column_name')` ではなく列値を参照する `length(column_name)` 系の SQL を生成するように整える。 +- SQLite driver の修正を公開 API へ漏らさず、driver 内部の SQL 生成責務に閉じ込める。 +- 長さ制約の既定値あり・既定値なしの両ケースで、`char` `string` `uuid` の意味論が崩れないことを確認できるテストを整備する。 +- 必要に応じて README / `documents/rdb/schema_builder.md` の SQLite 例を同期し、誤った `CHECK (length('email') <= 255)` 表記を残さない。 + +## 進捗 +- [x] `.cursor/rules/project.mdc` と `.cursor/rules/branch.mdc` を確認した +- [x] 現在のブランチ名に対応するブランチルールファイルが未作成であることを確認した +- [x] SQLite の `CHECK` 生成箇所を調査し、`create_column_query.nim` と `add_column_query.nim` の両方に同種の問題があることを確認した +- [x] 対象カラム型が `Column.char`, `Column.string`, `Column.uuid` の 3 系統であることを確認した +- [x] ドキュメントに同じ誤表記が残っていることを確認した +- [x] ブランチルールに改修方針・進捗粒度・参考資料を整理した +- [x] SQLite schema builder の `CHECK` 制約生成を、列名を文字列リテラル化しない形式へ修正する +- [x] `CREATE TABLE` 経路のテストを追加または更新し、生成 SQL が列値長を検査することを確認する +- [x] `ADD COLUMN` 経路のテストを追加または更新し、生成 SQL が列値長を検査することを確認する +- [x] `char` `string` `uuid` の既定長と明示長の両ケースで回帰確認する +- [x] 必要な README / `documents/rdb/schema_builder.md` の記述を同期する +- [ ] 実装途中で設計認識に誤りが見つかった場合は、本ファイルの `調査結果・設計まとめ` を更新する + +## 参考資料 +- `src/allographer/schema_builder/queries/sqlite/sub/create_column_query.nim` +- `src/allographer/schema_builder/queries/sqlite/sub/add_column_query.nim` +- `tests/sqlite/test_create_schema.nim` +- `tests/sqlite/test_schema.nim` +- `documents/rdb/schema_builder.md` +- `README.md` +- SQLite Documentation: CHECK constraints +- SQLite Documentation: core functions `length(...)` + +## 調査結果・設計まとめ + +### 背景 + +SQLite 実装では、文字列長を制限するために `CHECK (length(...) <= N)` を列定義へ付与している。 + +しかし現状の SQL 生成は `length('{column.name}')` となっており、列値ではなく列名そのものを単一引用符付きの文字列リテラルとして評価している。 + +その結果、たとえば `name` 列では常に `length('name') == 4` と評価されるため、期待した列値バリデーションにならない。 + +### 現状の問題 + +1. SQLite の `CREATE TABLE` 用列生成で `CHECK (length('{column.name}') <= {maxLength})` を出力している +2. SQLite の `ADD COLUMN` 用列生成でも同じ誤りを重複して持っている +3. 影響範囲が `Column.char`, `Column.string`, `Column.uuid` にまたがっている +4. ドキュメント例にも同じ誤表記があり、利用者が誤仕様と誤解する余地がある +5. SQL 生成の不具合であり、公開 API ではなく SQLite driver 内部責務として直すべき問題である + +### 設計目標 + +1. SQLite で生成される長さ `CHECK` 制約が常に列値を参照する +2. `char`, `string`, `uuid` の API 意味論を変えずに不具合のみを修正する +3. `create` と `add column` の両経路で SQL 生成規則を揃える +4. 既存の RDB 向け共通 API を汚さず、SQLite 実装へ変更を閉じ込める +5. テストと文書で回帰防止できる状態にする + +### 非目標 + +1. SQLite 以外の driver の長さ制約仕様を同時に変更すること +2. `Column.char`, `Column.string`, `Column.uuid` の既定長そのものを見直すこと +3. schema builder 全体の quoting 戦略をこのブランチで全面再設計すること +4. 公開 API の追加・削除・引数変更を行うこと + +### 実装方針 + +#### 1. SQLite の長さ `CHECK` 制約は列値参照に統一する + +変更対象: +- `src/allographer/schema_builder/queries/sqlite/sub/create_column_query.nim` +- `src/allographer/schema_builder/queries/sqlite/sub/add_column_query.nim` + +方針: +- `length('{column.name}')` を列値参照の式へ修正する +- `char`, `string`, `uuid` の 3 系統で同じルールを適用する +- `CREATE TABLE` と `ADD COLUMN` で生成式の差異を作らない + +#### 2. 列名の扱いは SQLite 列参照として妥当な形を維持する + +要点: +- 単一引用符 `'...'` は文字列リテラルなので使用しない +- 既存の SQLite SQL 生成スタイルに合わせ、列参照として解釈される形を採用する +- 必要なら identifier quoting の既存ルールを確認し、単純な文字列置換ではなく SQLite 方言として自然な表現に寄せる +- 共通 helper を `schema_utils` に置き、`CREATE TABLE` / `ADD COLUMN` の両経路で同じ生成規則を使う + +#### 3. テストは SQL 文字列と実動作の両面を意識して追加する + +候補: +1. schema 生成テストで `CHECK` 句が列値参照になっていることを確認する +2. `Column.string("name")` の既定長ケースを確認する +3. `Column.char("name", 20)` の明示長ケースを確認する +4. `Column.uuid("uuid")` の既定長ケースを確認する +5. `add()` 経路でも同じ `CHECK` 句になることを確認する + +### 受け入れ基準 + +1. SQLite の `char`, `string`, `uuid` が生成する `CHECK` 制約で列名が単一引用符付き文字列になっていない +2. `CREATE TABLE` と `ADD COLUMN` の両方で同じ修正が反映されている +3. 追加または更新したテストで不具合再発を防止できる +4. 必要なドキュメント例が実装と一致している +5. 公開 API 変更なしで SQLite driver 内に修正が閉じ込められている + +### 参照仕様 + +- SQLite Documentation: CHECK constraints + https://www.sqlite.org/lang_createtable.html#ckconst +- SQLite Documentation: Built-In Scalar SQL Functions + https://www.sqlite.org/lang_corefunc.html#length + +### リスク + +1. 列名参照の quoting 形式を誤ると、今度は予約語や特殊文字を含む列名で不整合が出る可能性がある +2. `create` と `add column` の片方だけ修正すると、経路差による再発が起きる +3. ドキュメント未更新のままだと、修正後も利用者が誤った SQL 例を参照して混乱する +4. SQL 文字列比較系テストだけだと、実際の制約評価の誤りを見逃す可能性がある + +### 段階導入案 + +1. 影響箇所と既存 SQLite テストを整理する +2. `create` / `add column` の SQL 生成を修正する +3. `char` / `string` / `uuid` の回帰テストを追加または更新する +4. 必要な README / documents を同期する +5. 実装差分が設計とずれたら本ファイルを更新する diff --git a/clients/nim-allographer/.cursor/rules/branch/320-allographerpostgresql-非同期待機改善-設計書.mdc b/clients/nim-allographer/.cursor/rules/branch/320-allographerpostgresql-非同期待機改善-設計書.mdc new file mode 100644 index 0000000..a05b3dc --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/320-allographerpostgresql-非同期待機改善-設計書.mdc @@ -0,0 +1,156 @@ +--- +description: 320-allographerpostgresql-非同期待機改善-設計書ブランチでの開発時に読み込む +alwaysApply: false +--- +allographer(PostgreSQL) 非同期待機改善 ブランチルール +=== + +このブランチで実装することは以下の通りです。 +- allographer の PostgreSQL 実装に残っている `sleepAsync(10)` ベースの非同期待機を、libpq ソケット readiness ベースの待機へ置き換える。 +- `query*`, `queryPlain*`, `exec*`, `getColumns*` など、同一問題を持つ待機経路を共通化して修正する。 +- `dbOpen` 時に PostgreSQL 接続を non-blocking 化し、libpq 非同期 API の前提を明示的に保証する。 +- 接続プール空き待機を `sleepAsync(10)` ポーリングから通知ベースへ移行し、プール枯渇時の待機遅延を抑制する。 +- 公開 API 互換を維持したまま、httpbeast/httpx 利用時の DB 完了後の回収遅延を縮小する。 + +## 進捗 +- [x] `src/allographer/query_builder/libs/postgres/postgres_impl.nim` の待機ループを調査し、`sleepAsync(10)` 依存箇所を一覧化する +- [x] `src/allographer/query_builder/libs/postgres/postgres_rdb.nim` を確認し、既存宣言で再利用できる libpq API と不足宣言を整理する(追加宣言不要。`pqsocket` / `pqflush` / `pqsetnonblocking` 等は既存) +- [x] PostgreSQL 待機ヘルパ `waitPgReadable` / `waitPgWritable` / `cancelQuery` の責務と配置を決める(`postgres_impl.nim` 内の非公開プロシージャ) +- [x] `PQsocket` / `PQconsumeInput` / `PQisBusy` / `PQgetResult` / `PQflush` に基づく共通クエリ待機ループを設計する(`pgFlushOutgoing` / `pgNextResult` / `pgEnsureIdle`) +- [x] `query*`, `queryPlain*`, `exec*`, `getColumns*` の各経路を共通待機ロジックへ寄せる +- [x] タイムアウト時の `cancelQuery` 実行とエラーメッセージ方針を統一する(タイムアウトは `cancelQuery` 後に `DbError`) +- [x] `src/allographer/query_builder/models/postgres/postgres_open.nim` で `pqsetnonblocking(conn, 1)` と検証処理を追加する +- [x] `src/allographer/query_builder/models/postgres/postgres_types.nim` に接続プール waiter キューを追加する(`waiters*: seq[Future[void]]`) +- [x] `src/allographer/query_builder/models/postgres/postgres_exec.nim` の `getFreeConn` / `returnConn` を通知ベースへ置換する +- [x] PostgreSQL の単体・結合・回帰テストを追加または更新する(`tests/postgres/test_pool_wait.nim` 追加、既存 `test*.nim` 回帰) +- [x] `sleepAsync(10)` が PostgreSQL クエリ待機とプール空き待機から除去されたことを確認する +- [ ] README / documents / ルール文書に仕様差分が必要か確認し、必要分を同期する(利用者向け公開 API 変更なしのため未着手。必要なら追記) +- [x] MariaDB: `mariadb_rdb.nim` に `mysql_read_query_result`(`cdecl`)を追加 +- [x] MariaDB: `mariadb_impl.nim` は接続ブロッキング前提で `mysql_real_query` / `mysql_read_query_result` / `mysql_fetch_row` / `free_result` を同期利用(`mysql_read_query_result_*` / `fetch_row_*` の async ループは環境でタイムアウト・デッドロックのため不使用) +- [x] MariaDB: `mariadb_open.nim` は `MYSQL_OPT_NONBLOCK` を付与しない(同上) +- [x] MariaDB: `mariadb_types` / `mariadb_exec` のプール空き待ちは通知ベース(`tests/mariadb/test_pool_wait.nim`) + +## 参考資料 +- allographer の PostgreSQL 実装: `src/allographer/query_builder/libs/postgres/postgres_impl.nim` +- allographer の PostgreSQL 実行管理: `src/allographer/query_builder/models/postgres/postgres_exec.nim` +- allographer の PostgreSQL 接続初期化: `src/allographer/query_builder/models/postgres/postgres_open.nim` +- allographer の PostgreSQL 型定義: `src/allographer/query_builder/models/postgres/postgres_types.nim` +- libpq 非同期 API: `PQsocket`, `PQconsumeInput`, `PQisBusy`, `PQgetResult`, `PQsetnonblocking`, `PQflush`, `PQgetCancel`, `PQcancel`, `PQfreeCancel` +- Nim `asyncdispatch` のイベントループ仕様 +- httpbeast / httpx の POSIX selector ループ実装 + +## 調査結果・設計まとめ + +### 背景 + +`httpbeast` / `httpx` 利用時のみ、DB クエリ待機が `asynchttpserver` より大きく悪化する事象がある。 + +現状の allographer PostgreSQL 実装は、libpq 非同期 API を使いながらも、実際の待機を `await sleepAsync(10)` に依存している。 + +### 問題の本質 + +- 現状の PostgreSQL 待機は、ソケット readiness 待ちではなく 10ms 刻みのポーリングである +- Nim の `asyncdispatch` は `poll` / `waitFor` / `runForever` が回らない限りタイマーも進まない +- httpbeast / httpx の POSIX 経路は `selectInto(-1, ...)` による無期限ブロックを中心としており、`asyncdispatch.poll(0)` は限定的にしか回らない +- そのため `sleepAsync(10)` は実時間 10ms で復帰せず、「次に HTTP 側ループがたまたま dispatcher を回すまで」遅延する +- 結果として、DB は完了済みでも allographer 側の結果回収が遅れ、体感レイテンシが悪化する + +### 設計目標 + +1. PostgreSQL クエリ待機から `sleepAsync` ポーリングを除去する +2. libpq ソケットの可読・可書込イベントで待機する +3. 接続プール空き待ちも通知ベースへ移行し、タイマー依存を減らす +4. `dbOpen`, `table`, `select`, `find`, `update` などの公開 API 互換を維持する + +### 非目標 + +1. Query Builder API の破壊的変更 +2. MySQL / SQLite への同時展開(MariaDB は PostgreSQL と同方針で非同期待機を実装済み) +3. 初回対応で全 OS 最適化を完了させること + +### 実装方針 + +#### 1. libpq 待機を FD イベント待機へ変更する + +変更対象: +- `src/allographer/query_builder/libs/postgres/postgres_impl.nim` +- `src/allographer/query_builder/libs/postgres/postgres_rdb.nim` + +追加する内部ヘルパ: +- `waitPgReadable(db: PPGconn, timeoutMs: int): Future[bool]` +- `waitPgWritable(db: PPGconn, timeoutMs: int): Future[bool]` +- `cancelQuery(db: PPGconn)` + +要点: +- `pqsocket(db)` で FD を得る +- 必要に応じて dispatcher に `AsyncFD` を登録する +- read / write readiness を Future 完了で待つ +- タイムアウト管理を行う +- 登録リークや未解除ハンドラを残さない + +#### 2. クエリ実行ループを共通化する + +送信フェーズ: +1. `pqsendQueryParams(...)` を呼ぶ +2. `pqflush == 1` の間は `waitPgWritable` で待機する + +受信フェーズ: +1. `pqconsumeInput` +2. `pqisBusy == 1` なら `waitPgReadable` で待機する +3. ready 後に `pqgetResult` を drain する + +タイムアウト時は `cancelQuery` を実行し、エラーを返す。 + +#### 3. `dbOpen` で non-blocking を保証する + +変更対象: +- `src/allographer/query_builder/models/postgres/postgres_open.nim` + +仕様: +1. `pqsetnonblocking(conn, 1)` を実行する +2. 失敗時は `DbError` +3. `pqisnonblocking(conn) == 1` を確認し、保証できない場合は例外化する + +#### 4. 接続プール空き待ちを通知ベースに変更する + +変更対象: +- `src/allographer/query_builder/models/postgres/postgres_types.nim` +- `src/allographer/query_builder/models/postgres/postgres_exec.nim` + +方針: +- `Connections` に `waiters*: Deque[Future[void]]` を追加する +- `getFreeConn` は空きがなければ waiter を登録して待機する +- `returnConn` は `isBusy = false` にした後、未完了 waiter を 1 つだけ起こす +- 余剰 waiter や完了済み waiter は掃除する + +### 失敗時挙動 + +1. `pqsocket < 0` は `DbError` +2. タイムアウト時は `cancelQuery` 実行後に `DbError` +3. `cancelQuery` 失敗時は追加情報付きで例外化 +4. dispatcher 登録・解除エラーは握り潰さず上位へ伝播する + +### テスト方針 + +単体テスト: +1. `waitPgReadable` が read event で復帰する +2. timeout 時に `false` を返す +3. `cancelQuery` が後始末まで行う +4. `getFreeConn` が通知で復帰する + +結合テスト: +1. httpbeast / httpx + Postgres のレイテンシ回帰を確認する +2. `asynchttpserver` と比較して極端な乖離がないことを確認する +3. プール枯渇時の待機が 10ms 刻みポーリングに依存しないことを確認する + +回帰テスト: +1. 既存 CRUD クエリの結果互換 +2. transaction 系 API の動作 +3. 例外時メッセージが意味を保っていること + +### 受け入れ基準 + +1. PostgreSQL クエリ待機ループから `await sleepAsync(10)` が除去されている +2. httpbeast / httpx で DB 完了後の回収遅延が顕著に増幅しない +3. プール空き待機で `sleepAsync` ポーリングを使わない +4. 公開 API 変更なしで既存テストと追加テストが成立する diff --git a/clients/nim-allographer/.cursor/rules/branch/321-mariadb-nonblocking.mdc b/clients/nim-allographer/.cursor/rules/branch/321-mariadb-nonblocking.mdc new file mode 100644 index 0000000..a346c0f --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/321-mariadb-nonblocking.mdc @@ -0,0 +1,240 @@ +--- +description: 321-mariadb-nonblockingブランチでの開発時に読み込む +alwaysApply: false +--- +MariaDB nonblocking API 対応 ブランチルール +=== + +このブランチで実装することは以下の通りです。 +- allographer の MariaDB 実装を、MariaDB Connector/C の non-blocking client API に追従できる構成へ設計・整備する。 +- 現在の `sleepAsync(10)` ベースの接続プール待機と、実質ブロッキングなクエリ実行経路を切り分けて改善方針を明確化する。 +- `mysql_real_query_start/cont` 系、`mysql_read_query_result_start/cont` 系、`mysql_fetch_row_start/cont` 系を使う待機ループの責務分離を設計する。 +- 必要な Connector/C 宣言を `mariadb_rdb.nim` に追加し、将来の実装変更でヘッダ不足が起きないようにする。 +- 公開 API 互換を維持しつつ、MariaDB driver 内に差分を閉じ込める。 +- 設計メモで整理していた背景・参照仕様・リスク・段階導入案をこのブランチルールへ集約し、参照元を一本化する。 + +## 進捗 +- [x] `.cursor/rules/project.mdc` と `.cursor/rules/branch.mdc` を確認した +- [x] 現行 `src/allographer/query_builder/models/mariadb/` と `src/allographer/query_builder/libs/mariadb/` を調査した +- [x] ローカルの `/usr/include/mariadb/mysql.h` で non-blocking API 宣言と待機フラグを確認した +- [x] MariaDB 公式ドキュメントで non-blocking API の制約と利用手順を確認した +- [x] ブランチルールに設計方針・改修粒度・参考資料を整理した +- [x] `src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim` に必要宣言を追加した +- [x] `mariadb_impl.nim` に共通待機ヘルパと `_start/_cont` ベースの実行ループを実装する +- [x] `mariadb_open.nim` で `MYSQL_OPT_NONBLOCK` を有効化し、接続初期化手順を整理する +- [x] `mariadb_types.nim` / `mariadb_exec.nim` のプール空き待機を通知ベースへ置換する +- [x] `tests/mariadb/` に pool wait と CRUD 回帰を追加する +- [x] 実装中に設計差分が出た場合は本ファイルの `調査結果・設計まとめ` を更新する +- [x] `src/allographer/query_builder/libs/surreal/` と `models/surreal/` の効率改善余地を調査し、`documents/rdb/surreal_efficiency_report.md` に整理した +- [x] `documents/rdb/surreal_efficiency_report.md` の P0/P1 項目に沿って、`surreal_impl.nim` の HTTP timeout 共通化、`surreal_open.nim` の bootstrap 1 回化、`surreal_lib.nim` の文字列生成改善、`surreal_exec.nim` の `getElems()` 化を実装した + +## 参考資料 +- `documents/rdb/mariadb_nonblocking_design.md` の内容を統合済み +- `src/allographer/query_builder/models/mariadb/mariadb_open.nim` +- `src/allographer/query_builder/models/mariadb/mariadb_exec.nim` +- `src/allographer/query_builder/models/mariadb/mariadb_types.nim` +- `src/allographer/query_builder/libs/mariadb/mariadb_impl.nim` +- `src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim` +- `documents/rdb/surreal_efficiency_report.md` +- `/usr/include/mariadb/mysql.h` +- MariaDB Documentation: Non-Blocking API Reference +- MariaDB Documentation: Using the Non-Blocking Library +- `src/allographer/query_builder/models/postgres/postgres_open.nim` +- `src/allographer/query_builder/models/postgres/postgres_exec.nim` + +## 調査結果・設計まとめ + +### 背景 + +現行の MariaDB 実装は `Future` を返しているが、実際の MariaDB Connector/C 呼び出しは `mysql_real_query()` / `mysql_use_result()` / `mysql_fetch_row()` を同期的に使っている。 + +そのため、Nim 側では async API に見えても、ソケット I/O 待機は Connector/C 内でブロックされうる。さらに接続プール枯渇時は `sleepAsync(10)` ポーリングで待っており、待機遅延が大きい。 + +### 現状の問題 + +1. `mariadb_exec.nim` の `getFreeConn` が `sleepAsync(10)` ポーリングで、プール返却の即時通知がない +2. `mariadb_impl.nim` の `query*` / `exec*` / `getColumns*` は non-blocking API を使っていない +3. `timeout` 引数はあるが、Connector/C の I/O 待機そのものをアプリ側 event loop で制御できていない +4. `mysql_use_result()` を使う都合上、行フェッチ途中で新規操作を開始できないという Connector/C 制約を、内部設計として明示できていない +5. 設計メモが別ファイルに分散すると、ブランチ進捗と参照資料の整合性が崩れやすい + +### 設計目標 + +1. MariaDB クエリ送受信を Connector/C の `_start/_cont` API へ置き換える +2. ソケット待機は `mysql_get_socket()` と `MYSQL_WAIT_*` に従って event loop で処理する +3. `mysql_get_timeout_value_ms()` に従って timeout を扱い、ライブラリ内部 timeout と allographer の接続 timeout を両立させる +4. 公開 API は維持し、driver 内部だけで差分を吸収する +5. 接続プール空き待機は PostgreSQL と同様の通知ベースへ寄せる + +### 非目標 + +1. Query Builder の公開 API 変更 +2. MySQL driver まで同時に全面改修すること +3. 初回対応で prepared statement 系まで non-blocking 化すること +4. このブランチの責務を MariaDB driver の範囲から広げすぎること + +### 実装方針 + +#### 1. `mariadb_rdb.nim` に non-blocking API 宣言を揃える + +追加または有効化する対象: +- `mysql_options` +- `mysql_get_socket` +- `mysql_get_timeout_value` +- `mysql_get_timeout_value_ms` +- `mysql_real_connect_start/cont` +- `mysql_real_query_start/cont` +- `mysql_store_result_start/cont` +- `mysql_read_query_result_start/cont` +- `mysql_fetch_row_start/cont` +- `mysql_free_result_start/cont` +- `MYSQL_WAIT_READ/WRITE/EXCEPT/TIMEOUT` +- 必要なら `mysql_optionsv` など周辺宣言も不足なく追加する + +#### 2. `mariadb_open.nim` で non-blocking を明示的に有効化する + +手順: +1. `mysql_init()` 後、`mysql_options(conn, MYSQL_OPT_NONBLOCK, nil)` を呼ぶ +2. 接続自体も `mysql_real_connect_start/cont` に寄せるか、初手は接続のみ同期で残すかを選ぶ +3. 設計上は接続も non-blocking に揃えるのが一貫するため、最終的には `_start/_cont` 化を目標とする + +補足: +- MariaDB 公式ドキュメントでは、`MYSQL_OPT_NONBLOCK` を有効化せずに non-blocking API を使うとクラッシュしうる +- DNS 解決は非同期化されないため、ホスト名利用時は依然として初回接続でブロックしうる +- 接続初期化の最終形は `_start/_cont` へ寄せるが、既存動作を壊さない段階導入を優先する + +#### 3. `mariadb_impl.nim` に共通待機ヘルパを追加する + +候補ヘルパ: +- `waitMariadb(conn: PMySQL, waitStatus: cint, deadline: MonoTime): Future[cint]` +- `runMariadbOp[T](...)` のような `_start/_cont` 反復ヘルパ +- `readAllRows(...)` のような result drain ヘルパ + +待機仕様: +- `waitStatus` の `MYSQL_WAIT_READ/WRITE/EXCEPT` を `AsyncFD` で待つ +- `MYSQL_WAIT_TIMEOUT` が立っているときは `mysql_get_timeout_value_ms()` を優先する +- allographer 側 `timeout` を超えたら `DbError` を返す +- 待機ループは API ごとに重複させず、共通の進行関数へ寄せる + +#### 4. クエリ実行は「送信」「結果確定」「結果取得」「行フェッチ」に分ける + +`SELECT` 系: +1. `mysql_real_query_start/cont` +2. `mysql_use_result()` または `mysql_store_result_start/cont` のどちらを使うか判断 +3. 既存実装との互換を優先するなら、まずは `mysql_use_result()` を維持しつつ `mysql_fetch_row_start/cont` で逐次取得する +4. 行取得後に `mysql_free_result_start/cont` または `mysql_free_result()` で解放する + +`INSERT/UPDATE/DELETE` 系: +1. `mysql_real_query_start/cont` +2. `mysql_real_query_start/cont` の完了を実行完了とみなし、結果セットがない経路では追加の `read_query_result` 呼び出しを行わない +3. `mysql_errno()` / `mysql_error()` を確認して `DbError` + +補足: +- MariaDB 公式ドキュメントでは、`mysql_use_result()` 自体はブロックしないが、そこからの `mysql_fetch_row_start/cont` は `use_result` 由来なら待機を伴う +- `store_result` を選ぶとフェッチ自体は即時化しやすい一方、結果全件受信を先に待つ必要がある +- 現行の逐次処理に近いのは `use_result + fetch_row_start/cont` +- 実装上の都合で `store_result` を選ぶ場合でも、公開 API の見え方は変えない +- 実装検証では、`mysql_real_query_start/cont` 直後に `mysql_read_query_result_start/cont` を追加すると DML 経路でタイムアウトが発生したため、`exec*` 系は `real_query` 完了をもって終了判定する方針へ更新した + +#### 4.1 実装差分メモ(2026-03-23) + +- `mariadb_rdb.nim` の `MYSQL_OPT_NONBLOCK` は MariaDB ヘッダ定義に合わせて `6000` を明示指定した +- `mariadb_impl.nim` では `waitMariadb` を共通化し、`mysql_real_query_start/cont` と `mysql_fetch_row_start/cont` / `mysql_free_result_start/cont` を統一ループで実行する実装にした +- `mariadb_open.nim` で `mysql_options(conn, MYSQL_OPT_NONBLOCK, nil)` を全接続で有効化した +- `mariadb_types.nim` / `mariadb_exec.nim` で接続プール待機を `waiters` ベースへ置換し、`sleepAsync(10)` ポーリングを撤廃した +- `tests/mariadb/test_pool_wait.nim` を追加し、`tests/mariadb/test_query.nim` / `tests/mariadb/test_transaction.nim` と合わせて回帰確認した + +#### 4.2 効率改善差分メモ(2026-03-23) + +- `mariadb_impl.nim`: `setColumnInfo` を `while true` フェッチループの外に移動し、行ごとに `fetch_field_direct` の FFI 呼び出しと文字列アロケーションが発生する問題を解消した。各行では `baseColumns` のコピーに対して NULL 上書きのみ行う +- `mariadb_impl.nim`: 全 proc から `assert db.ping == 0` を除去した。`mysql_ping` は同期ブロッキングでイベントループを停止するため。接続異常は `real_query_start` のエラーハンドリングで検出する +- `mariadb_impl.nim`: `epochTime()` ベースのデッドライン管理を `std/monotimes` の `MonoTime` + `Duration` に置き換え、NTP 補正による巻き戻りリスクを排除した +- `mariadb_impl.nim`: `MariadbWaitState` を `ref object` から `object`(値型)に変更し、`waitMariadb` 呼び出しあたり 1 回のヒープアロケーションを削減した +- `mariadb_impl.nim`: JSON→string 変換ロジックを `jsonObjValuesToStrSeq` / `jsonFlatToStrSeq` の 2 proc に集約し、4 箇所の重複を解消した +- `mariadb_impl.nim`: `getColumnTypes` の SQL を文字列補間から `?` プレースホルダに変更し、SQL インジェクションリスクを排除した +- `mariadb_exec.nim`: `exec` / `insertId` が DML のたびに `INFORMATION_SCHEMA.COLUMNS` を問い合わせていた問題を、`Connections.columnTypeCache` によるテーブル単位キャッシュで解消した +- `mariadb_types.nim`: `waiters` を `seq[Future[void]]` から `Deque[Future[void]]` に変更し、`wakeOnePoolWaiter` の先頭削除を O(n) → O(1) に改善した +- `mariadb_lib.nim`: `dbFormat` の char-by-char 連結を、`?` 位置間のサブ文字列を一括 `add` する方式に変更した +- SurrealDB: `surreal_impl.nim` に `runSurrealSql` / `awaitWithTimeout` を追加し、`query` / `exec` / `info` の HTTP timeout と JSON error 処理を共通化した +- SurrealDB: `surreal_open.nim` で `/status` と `/sql` の bootstrap を timeout 対応で実行し、namespace/database 定義を初回接続へ集約した +- SurrealDB: `surreal_lib.nim` の `numToAlphabet` / `questionToDaller` / `dbFormat(JsonNode)` を単一バッファ志向へ整理した +- SurrealDB: `surreal_exec.nim` の `getAllRows` は `rows.getElems()` を使い、`toSeq()` の追加走査を避けた + +#### 5. 接続プール待機は通知ベースへ置換する + +`mariadb_types.nim`: +- `Connections.waiters*: seq[Future[void]]` を追加する + +`mariadb_exec.nim`: +- `getFreeConn` は waiter を積んで `withTimeout` で待つ +- `returnConn` は接続返却後に 1 件だけ waiter を起こす +- PostgreSQL 実装と同じ責務分割に揃える +- waiters の起床順やキャンセル時の取り扱いは、既存 PostgreSQL 実装の方針に合わせて整理する + +#### 6. 段階導入する + +第1段階: +- ヘッダ宣言追加 +- ブランチルール・設計書追加 + +第2段階: +- プール待機の通知化 +- `exec*` 系の non-blocking 化 + +第3段階: +- `query*` / `rawQuery*` / `getColumns*` / `insertId` の非同期 result drain 実装 +- テスト拡充 +- 段階ごとに実装差分が出たら本ファイルへ追記し、ブランチ内の唯一の設計記録にする + +### 受け入れ基準 + +1. MariaDB driver で `sleepAsync(10)` によるプール待機がなくなっている +2. `mariadb_impl.nim` のクエリ待機が `mysql_real_query_start/cont` などの non-blocking API を利用している +3. 同一接続上で未完了 non-blocking 操作を残したまま次の操作を始めない +4. 既存 CRUD / transaction / schema テストが回帰しない +5. 公開 API 変更なしで実装差分が driver 内に閉じ込められている + +### 参照仕様 + +- MariaDB Documentation: Non-Blocking API Reference + https://mariadb.com/docs/server/reference/product-development/mariadb-internals/using-mariadb-with-your-programs-api/non-blocking-client-library/non-blocking-api-reference +- MariaDB Documentation: Using the Non-Blocking Library + https://mariadb.com/docs/server/reference/product-development/mariadb-internals/using-mariadb-with-your-programs-api/non-blocking-client-library/using-the-non-blocking-library +- `/usr/include/mariadb/mysql.h` + +確認済み項目: +- `MYSQL_OPT_NONBLOCK` +- `MYSQL_WAIT_READ`, `MYSQL_WAIT_WRITE`, `MYSQL_WAIT_EXCEPT`, `MYSQL_WAIT_TIMEOUT` +- `mysql_options`, `mysql_optionsv` +- `mysql_get_socket` +- `mysql_get_timeout_value`, `mysql_get_timeout_value_ms` +- `mysql_real_connect_start/cont` +- `mysql_real_query_start/cont` +- `mysql_store_result_start/cont` +- `mysql_read_query_result_start/cont` +- `mysql_fetch_row_start/cont` +- `mysql_free_result_start/cont` + +### リスク + +1. `mysql_use_result()` は結果を最後まで読み切る前に次操作へ進めないため、clean up 漏れがあると接続を壊しやすい +2. MariaDB non-blocking API は `_cont()` を最後まで呼び切る必要があり、途中中断設計を誤ると不整合が出る +3. DNS 解決はブロックしうるので、接続初期化だけは期待したほど改善しない可能性がある +4. MySQL driver と共通化したくなっても、まずは MariaDB driver 内に閉じ込めてから判断すべき +5. 設計記録を別ファイルに残さないことで、進捗と方針の乖離を抑える + +### 段階導入案 + +1. 設計書とブランチルールを追加 +2. `mariadb_rdb.nim` に宣言追加 +3. プール待機通知化 +4. `exec*` 系を non-blocking 化 +5. `query*` / `rawQuery*` / `getColumns*` を non-blocking 化 +6. 必要なら `mysql_real_connect_start/cont` を導入 + +### 完了条件 + +1. MariaDB driver の内部待機が Connector/C non-blocking API ベースになっている +2. プール空き待機が通知ベースである +3. 既存公開 API を壊していない +4. MariaDB の主要テストが通る diff --git a/clients/nim-allographer/.cursor/rules/branch/323-database-url-for-connection.mdc b/clients/nim-allographer/.cursor/rules/branch/323-database-url-for-connection.mdc new file mode 100644 index 0000000..02541b1 --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/323-database-url-for-connection.mdc @@ -0,0 +1,174 @@ +--- +description: 323-database-url-for-connectionブランチでの開発時に読み込む +alwaysApply: false +--- +dbOpen Database URL 対応 ブランチルール +=== + +このブランチで実装することは以下の通りです。 +- `dbOpen` に database URL を使って接続できる新しいオーバーロードを追加し、既存の引数シグネチャは後方互換のため残す。 +- SQLite / MySQL / MariaDB / PostgreSQL は一般的な database URL 仕様に従い、scheme・認証情報・host・port・database path・query を解釈できる設計に整える。 +- URL 解析責務を各 driver の `dbOpen` 実装にベタ書きせず、共通化できる層へ寄せて公開 API を汚さない。 +- 既存の URL 文字列 1 引数版 `dbOpen` と、新しく追加する引数デザインの役割分担を明確にし、曖昧なオーバーロード解決を避ける。 +- 既存テスト・example・README・`documents/rdb/query_builder.md` を、最終仕様と齟齬がない状態へ同期する。 + +## 進捗 +- [x] `.cursor/rules/project.mdc` と `.cursor/rules/branch.mdc` を確認した +- [x] 現在のブランチ名に対応するブランチルールファイルが未作成であることを確認した +- [x] 既存 `dbOpen` 実装が SQLite / MySQL / MariaDB / PostgreSQL / SurrealDB でどう分かれているか確認した +- [x] MySQL / MariaDB / PostgreSQL にはすでに URL 文字列 1 引数版 `dbOpen` があり、SQLite には未整備であることを確認した +- [x] 既存 URL 実装が `split` ベースの簡易パーサであり、一般的な URL 仕様を十分に扱えていないことを確認した +- [x] 今回のブランチでは SurrealDB を対象外とする方針を反映した +- [x] ブランチルールに改修方針・進捗粒度・参考資料を整理した +- [x] 一般的な database URL 仕様として採用する範囲を整理する +- [x] 新しい `dbOpen` オーバーロードの引数デザインを確定する +- [x] URL 解析の共通 helper / 型配置を決め、driver 差分の閉じ込め方を整理する +- [x] SQLite の URL 受け口を追加し、ファイルパス指定との意味論を衝突させない +- [x] MySQL / MariaDB / PostgreSQL の URL 解析を一般仕様に合わせて強化する +- [x] 正常系・異常系・後方互換のテストを driver ごとに追加または更新する +- [x] example / README / documents を同期する +- [x] 実装途中で設計認識に誤りが見つかった場合は、本ファイルの `調査結果・設計まとめ` を更新する + +## 参考資料 +- `src/allographer/connection.nim` +- `src/allographer/query_builder/models/sqlite/sqlite_open.nim` +- `src/allographer/query_builder/models/mysql/mysql_open.nim` +- `src/allographer/query_builder/models/mariadb/mariadb_open.nim` +- `src/allographer/query_builder/models/postgres/postgres_open.nim` +- `src/allographer/query_builder/models/surreal/surreal_open.nim` +- `tests/mysql/test_open.nim` +- `tests/mariadb/test_open.nim` +- `tests/postgres/test_open.nim` +- `tests/sqlite/connections.nim` +- `tests/postgres/connections.nim` +- `tests/mysql/connections.nim` +- `tests/mariadb/connections.nim` +- `example/connections.nim` +- `README.md` +- `documents/rdb/query_builder.md` +- RFC 3986: Uniform Resource Identifier (URI): Generic Syntax +- PostgreSQL Documentation: Connection URIs +- MySQL / MariaDB ecosystem documentation for connection URI / DSN conventions + +## 調査結果・設計まとめ + +### 背景 + +現状の `dbOpen` は、RDB 系では「分解済みの接続情報を個別引数で渡す形」が基本で、一部 driver にだけ URL 文字列 1 引数版が追加されている。 + +ただし既存の URL 版は `split` を重ねた簡易実装で、一般的な URL で登場する予約文字・query・省略可能項目・scheme 差異に弱い。API としては URL 対応を名乗りつつ、仕様が狭く不均一な状態である。 + +今回の目的は、既存 API を壊さずに、database URL を正式な接続入力として扱える `dbOpen` オーバーロード群を整備し、RDB driver 間の設計を揃えることである。SurrealDB は今回の対象外とする。 + +### 現状の問題 + +1. SQLite はファイルパス引数のみで、URL ベース接続の入口がない +2. MySQL / MariaDB / PostgreSQL の URL 版は実装されているが、一般的な URL 仕様を十分に扱えない +3. driver ごとに URL の扱いがバラバラで、共通 API として学習コストが高い +4. 既存の URL 文字列 1 引数版と、新たな「database url を使う関数」の住み分けを設計しないと、将来の拡張で曖昧さが増える + +### 設計目標 + +1. 既存 `dbOpen` シグネチャを残しつつ、新しいオーバーロードで URL ベース接続を自然に表現できる +2. SQLite / MySQL / MariaDB / PostgreSQL は一般的な URI 記法を優先する +3. URL 解析と driver ごとの接続情報変換を責務分離し、公開 API へ場当たり的分岐を持ち込まない +4. 後方互換・テスト・文書更新まで含めて、利用者が迷わない状態を作る + +### 非目標 + +1. 既存の個別引数版 `dbOpen` を削除または非推奨化すること +2. 接続 URL と無関係な query builder / schema builder API を同時に変更すること +3. SurrealDB の接続モデルや URL 設計をこのブランチで見直すこと +4. このブランチで ORM 的な接続設定 abstraction を全面導入すること + +### API 方針 + +#### 1. 新しいオーバーロードは「URL であることが呼び出し側から明確に分かる」形を優先する + +候補: +1. `dbOpen(MySQL, url = "...", maxConnections = ..., timeout = ...)` +2. `dbOpen(MySQL, databaseUrl = "...", maxConnections = ..., timeout = ...)` +3. URL 専用の薄い型を導入して `dbOpen(MySQL, DatabaseUrl("..."), ...)` + +採用した設計: +- URL 専用の薄い型 `DatabaseUrl` を導入し、`dbOpen(driver, databaseUrl = asDatabaseUrl("..."), ...)` で呼べるようにした +- 既存の `dbOpen(driver, url: string, ...)` は後方互換のため残し、内部では共通 parser に委譲する +- SQLite の既存 `dbOpen(SQLite3, path: string, ...)` はそのまま維持し、URL 文字列が渡された場合のみ URL ルートへ寄せる + +理由: +- SQLite では単なる `string` がファイルパスと URL の両方になり得る +- `DatabaseUrl` に分けることで、既存の string overload と overload 解決が衝突しない +- 将来の拡張でも、URL 専用の入口を安全に追加しやすい + +#### 2. RDB の URL 仕様は一般的な形式を優先する + +最低限そろえる対象: +1. `sqlite:///absolute/path/to/db.sqlite3` +2. `sqlite://relative/path.db` またはこの扱いを不採用とするなら明文化する +3. `mysql://user:pass@host:3306/database` +4. `mariadb://user:pass@host:3306/database` +5. `postgresql://user:pass@host:5432/database` + +設計メモ: +- `postgres://...` は `postgresql://...` の alias として受ける +- percent-encoding の decode 要否を明確にする +- query parameter は現段階で未使用でも、少なくとも破壊的に誤解釈しない方針を取る +- database 名や password に `:` `/` `@` が含まれるケースを、単純 `split` で壊さない + +### 実装方針 + +#### 1. URL 解析は共通 helper 化を優先する + +方針: +- `scheme`, `user`, `password`, `host`, `port`, `path`, `query` を扱える共通 parser を用意する +- URL 専用の `DatabaseUrl` を導入し、driver ごとの差分は「解析結果からどう接続情報へ落とすか」に限定する +- 既存 driver ファイルの `split` ベース処理は置き換え対象とする + +#### 2. SQLite は URL とファイルパスの意味論を分ける + +要点: +- 既存 `dbOpen(SQLite3, "/path/to/file.db")` はそのまま維持する +- URL 版は `asDatabaseUrl(...)` で「これは URL」と明示できる形を優先する +- `:memory:` や相対パスとの整合を壊さない + +#### 3. 既存 URL 1 引数版は互換維持しつつ内部実装を新 parser に寄せる + +要点: +- MySQL / MariaDB / PostgreSQL の既存テストは維持する +- 既存の `ValueError` / `DbError` の期待が妥当なら保つ +- 公開 API は残しつつ、仕様の狭さだけを改善する + +#### 4. テストは「構文解析」と「実接続」を分けて考える + +候補: +1. URL から接続情報へ正しく分解されることを確認する軽量テスト +2. 既存の integration 系 `dbOpen(...).isConnected()` テスト +3. 不正 scheme・不足情報・不正 port・予約文字を含む入力の異常系テスト +4. SQLite の URL 指定とファイルパス指定の後方互換テスト +5. SurrealDB の namespace / database 解決規則テスト + +### 受け入れ基準 + +1. 既存の `dbOpen` 呼び出しを壊さずに、新しい URL ベースのオーバーロードを追加できている +2. SQLite / MySQL / MariaDB / PostgreSQL の URL 設計が一般的な仕様と整合している +3. SurrealDB の URL 設計が独自要件を満たしつつ、他 driver と読後感が大きく乖離していない +4. 既存の簡易 `split` 実装を置き換え、予約文字や query を誤解釈しにくい構造になっている +5. driver ごとの正常系・異常系・後方互換テストが追加または更新されている +6. example / README / documents / ブランチルールが最終仕様と一致している + +### リスク + +1. Nim 標準ライブラリだけで URL 解析を行う場合、RFC 3986 の細部を十分に扱えない可能性がある +2. SQLite の URL 仕様を曖昧にすると、既存のファイルパス API と混同されやすい +3. SurrealDB の namespace / database の載せ方を誤ると、RDB に寄せたつもりで逆に分かりにくくなる +4. 既存の `dbOpen(driver, urlString)` と新オーバーロードが衝突すると、Nim の overload 解決で利用者体験が悪化する +5. 文字列解析だけでなく実接続テストも必要なため、driver 横断で確認コストが上がる + +### 段階導入案 + +1. 採用する URL 仕様と新オーバーロードの表面 API を確定する +2. 共通 URL parser / 接続情報変換レイヤを用意する +3. MySQL / MariaDB / PostgreSQL の既存 URL 実装を載せ替える +4. SQLite の URL 入口を追加する +5. テストを整備して後方互換と異常系を固める +6. example / README / documents を同期する diff --git a/clients/nim-allographer/.cursor/rules/branch/333-prepared-statement.mdc b/clients/nim-allographer/.cursor/rules/branch/333-prepared-statement.mdc new file mode 100644 index 0000000..c84330b --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/333-prepared-statement.mdc @@ -0,0 +1,167 @@ +--- +description: 333-prepared-statementブランチでの開発時に読み込む +alwaysApply: false +--- +333-prepared-statement ブランチでの高速化内容 +=== + +このブランチで実装することは以下の通りです。 +- prepared statement を、高頻度な同一 SQL 実行で通常経路より速くなるように改善する +- とくに PostgreSQL で、prepare/deallocate の無駄と接続拡散を減らす +- benchmark が prepared statement の warm 状態を正しく測れるようにする + +## 進捗 + +- [x] `example/benchmark.nim` の PostgreSQL 計測で prepared update が遅く見える理由を調査した +- [x] prepared statement 高速化の設計方針を整理した +- [ ] PostgreSQL にプール単位の prepared statement cache を追加する +- [ ] `close()` を論理 close 化し、物理破棄は別 API に分離する +- [ ] 接続固定で prepared statement を連続実行できる API を追加する +- [ ] `example/benchmark.nim` を cold/warm 計測に分離し、warm 経路で同一 statement を再利用する +- [ ] prepared cache と接続固定 API のテストを追加する + +## 参考資料 + +- PostgreSQL libpq prepared statements: https://www.postgresql.jp/document/12/html/libpq-exec.html + - `PQsendPrepare` + - `PQsendQueryPrepared` + - `DEALLOCATE` +- PostgreSQL 非同期 libpq: https://www.postgresql.jp/document/12/html/libpq-async.html +- 対象実装 + - `src/allographer/query_builder/models/postgres/postgres_exec.nim` + - `src/allographer/query_builder/models/postgres/postgres_types.nim` + - `src/allographer/query_builder/libs/postgres/postgres_impl.nim` + - `example/benchmark.nim` + +## 調査結果・設計まとめ + +### 現状の問題 + +PostgreSQL で prepared statement が遅く見える主因は、prepared statement 自体の性質ではなく、現在の実装と benchmark の計測条件にある。 + +- `benchmark.nim` は計測のたびに `prepare` と `close` を行っており、steady-state の再利用性能ではなくライフサイクル全体を測っている +- PostgreSQL 実装は prepared statement をコネクションごとに lazy prepare するため、プールが大きいと `PREPARE` が広く拡散する +- `first()` と `exec()` が毎回 `getFreeConn()` するため、1 つの業務操作でも接続が変わり、prepared state の局所性が弱い +- 通常経路も `PQsendQueryParams` による bind 実行なので、prepared の利得は parse/plan 再利用ぶんに限られる + +### 高速化の基本方針 + +prepared statement を速くするには、単発 API を追加するだけでは足りない。以下をまとめて入れる必要がある。 + +- prepare/deallocate の回数を減らす +- 同じ logical operation を同じ接続に寄せる +- warm 済み prepared state をプール内で継続利用する + +### 設計方針 1: プール単位の prepared cache + +`PreparedStatement` ハンドルごとに物理 state を持つのではなく、`Connections` が SQL ごとの prepared state を保持する。 + +概念設計: + +```nim +type PostgresPreparedEntry = ref object + sql*: string + nArgs*: int + stmtBaseName*: string + stmtNames*: seq[string] ## connI ごとの物理 prepared 名 + refCount*: int + lastUsedAt*: int64 + +type Connections* = ref object + conns*: seq[Connection] + timeout*: int + waiters*: Deque[Future[void]] + columnTypeCache*: Table[string, seq[Row]] + preparedCache*: Table[string, PostgresPreparedEntry] +``` + +期待効果: + +- 同じ SQL に対する `prepare()` の重複を避けられる +- 同一プール内では physical prepare を接続ごとに 1 回へ寄せられる +- 短命な prepared handle を繰り返し作っても warm state を失いにくくなる + +### 設計方針 2: `close()` の論理 close 化 + +現在の `close()` は、その handle が使った接続すべてに `DEALLOCATE` を送る。高速化の観点ではこれは不利なので、意味論を分ける。 + +- `close()`: handle の参照を閉じるだけ +- `clearPreparedCache()` または `flushPrepared(sql)`: 物理 `DEALLOCATE` +- 接続 close 時: その接続に紐づく prepared state を一括破棄 + +期待効果: + +- 利用者が毎回 `prepare` / `close` しても、プール側は hot SQL を保持できる +- benchmark の warm 計測でも、毎回の物理破棄コストを避けられる + +### 設計方針 3: 接続固定 API の追加 + +prepared statement の locality を高めるため、同じ接続上で複数回の prepared 実行を束ねる API を追加する。 + +候補 API: + +```nim +proc withPreparedConnection*( + self: PostgresConnections, + body: proc (ctx: PostgresPreparedContext): Future[void] +): Future[void] +``` + +```nim +type PostgresPreparedContext* = ref object + owner*: PostgresConnections + connI*: int +``` + +prepared statement 側には `ctx` 付き overload を足す。 + +```nim +proc first*(self: PostgresPreparedStatement, ctx: PostgresPreparedContext, args: seq[string]): Future[Option[JsonNode]] +proc exec*(self: PostgresPreparedStatement, ctx: PostgresPreparedContext, args: seq[string]): Future[void] +``` + +期待効果: + +- `SELECT` と `UPDATE` が別接続に飛ぶのを防げる +- lazy prepare の発生先が必要な接続に限定される +- transaction 中の `transactionConn` と整合しやすい + +### benchmark の変更方針 + +`example/benchmark.nim` は prepared statement の高速化効果を正しく測れるように変更する。 + +- `selectStmt` / `updateStmt` を `timeProcess` の外で 1 回だけ作る +- 繰り返し計測では同じ prepared statement を再利用する +- `close()` は全計測の最後に 1 回だけ呼ぶ +- benchmark 名を `cold` と `warm` に分ける +- warm 計測では `withPreparedConnection` を使って接続固定経路を測る + +### 実装順序 + +#### Phase 1 + +- `Connections.preparedCache` を追加する +- `prepare()` を cache lookup ベースに変更する +- `close()` を論理 close 化する +- `clearPreparedCache()` を追加する + +#### Phase 2 + +- `PostgresPreparedContext` を追加する +- `withPreparedConnection` を追加する +- prepared `get/first/exec` に `ctx` overload を追加する +- `benchmark.nim` を warm/cold 計測へ更新する + +#### Phase 3 + +- `preparedCacheMaxEntries` を追加する +- `refCount == 0` の entry を対象に LRU eviction を入れる +- eviction 時に `DEALLOCATE` を発行する + +### テスト方針 + +- 同じ SQL を複数回 `prepare()` しても、同一接続で physical prepare が 1 回に収まること +- `close()` 後に再 `prepare()` しても、cache が残る設計なら physical prepare が増えないこと +- `withPreparedConnection` の block 内で同じ `connI` が使われること +- transaction 中は `transactionConn` が優先されること +- cache eviction 時に `DEALLOCATE` が正しく走ること diff --git a/clients/nim-allographer/.cursor/rules/branch/336-update-prepared-statement.mdc b/clients/nim-allographer/.cursor/rules/branch/336-update-prepared-statement.mdc new file mode 100644 index 0000000..41b40b0 --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/336-update-prepared-statement.mdc @@ -0,0 +1,206 @@ +--- +description: 336-update-prepared-statementブランチでの開発時に読み込む +alwaysApply: false +--- +336-update-prepared-statement ブランチでの prepared statement 横展開 +=== + +このブランチで実装することは以下の通りです。 +- 既存の SQLite / PostgreSQL / MySQL / MariaDB の prepared statement API を参照しつつ、SurrealDB 向け prepared statement API を追加する +- SurrealDB v3 の `/sql` API と SurrealQL の `LET` / parameter 仕様を前提に、server-side prepare ではなく client-side template reuse と 1 リクエスト多文実行で prepared statement 的な使い勝手を実現する +- SurrealDB 固有事情は `models/surreal` と `libs/surreal` に閉じ込め、公開 API は既存 driver と可能な限り揃える +- このターンでは実装は行わず、調査結果と実装方針の設計書を固める + +## 進捗 + +- [x] `.cursor/rules/project.mdc` / `.cursor/rules/branch.mdc` / `.cursor/rules/branch/336-update-prepared-statement.mdc` を確認した +- [x] 既存 prepared statement API の到達点を SQLite / PostgreSQL / MariaDB / MySQL で確認した +- [x] 現在の SurrealDB 実装と `/sql` 利用箇所を確認した +- [x] SurrealDB v3 公式ドキュメントで parameter / HTTP `/sql` / transaction の仕様を確認した +- [x] SurrealDB prepared statement の設計と利用例を `documents/surrealdb/prepared_statement.md` に整理した +- [x] `models/surreal` に prepared statement 用の型と公開 API を追加した +- [x] `libs/surreal` に multi-statement 実行向けの共通処理を追加した +- [x] SurrealDB prepared statement のテストを追加した +- [x] `documents/surrealdb/query_builder.md` / `documents/rdb/query_builder.md` を更新した + +## 参考資料 + +- 既存 prepared statement API + - `src/allographer/query_builder/models/sqlite/sqlite_types.nim` + - `src/allographer/query_builder/models/sqlite/sqlite_exec.nim` + - `src/allographer/query_builder/models/postgres/postgres_types.nim` + - `src/allographer/query_builder/models/postgres/postgres_exec.nim` + - `src/allographer/query_builder/models/mysql/mysql_exec.nim` + - `src/allographer/query_builder/models/mariadb/mariadb_exec.nim` +- 現在の SurrealDB 実装 + - `src/allographer/query_builder/models/surreal/surreal_types.nim` + - `src/allographer/query_builder/models/surreal/surreal_exec.nim` + - `src/allographer/query_builder/models/surreal/surreal_open.nim` + - `src/allographer/query_builder/libs/surreal/surreal_impl.nim` + - `src/allographer/query_builder/libs/surreal/surreal_lib.nim` + - `src/allographer/schema_builder/queries/surreal/schema_utils.nim` +- 既存ドキュメント + - `documents/surrealdb/query_builder.md` + - `documents/surrealdb/schema_builder.md` +- SurrealDB v3 公式ドキュメント + - Parameters: https://surrealdb.com/docs/surrealql/parameters + - HTTP Protocol (`POST /sql`): https://surrealdb.com/docs/surrealdb/integration/http + - Transactions: https://surrealdb.com/docs/surrealql/transactions + - Security summary (parameterized query): https://surrealdb.com/docs/surrealdb/security/summary + +## 調査結果・設計まとめ + +### 1. 前提整理 + +SurrealDB v3 には PostgreSQL や MySQL のような server-side prepared statement を、そのまま同じ意味で `/sql` HTTP 経由に載せる公開 API は見当たらない。一方で、以下は公式仕様として確認できる。 + +- `/sql` は 1 回の POST で複数の SurrealQL 文を `;` 区切りで送れる +- parameter は `LET $x = ...;` で定義して後続文から参照できる +- SurrealDB 3.x では `$x = ...` だけの旧記法は deprecated で、`LET` を使う必要がある +- statement はデフォルトで文ごとに transaction 境界を持つ。複数文を原子的に扱いたい場合は `BEGIN TRANSACTION; ...; COMMIT TRANSACTION;` が必要 + +したがって、このブランチでの `prepare()` は「DB が statement を事前コンパイルして保持する API」ではなく、「SQL template の正規化・再利用・安全な値埋め込み・単一 request 化を行う client-side prepared statement」として設計する。 + +### 2. 公開 API 方針 + +利用者体験は既存 driver に寄せる。第一候補は次の API である。 + +```nim +let stmt = surreal.prepare("SELECT * FROM user WHERE id = ?") +let rows = await stmt.get(@["user:alice"]) +let row = await stmt.first(%*[1]) +await stmt.exec(@["value"]) +await stmt.close() +await surreal.clearStmtCache() +await surreal.withConn(proc(ctx: SurrealPreparedContext): Future[void] {.async.} = + discard await stmt.first(ctx, @["user:alice"]) +) +``` + +採用方針: + +- `prepare` +- `get` +- `first` +- `exec` +- `close` +- `flushStmt` +- `clearStmtCache` +- `withConn` +- `SurrealPreparedContext` + +初期スコープでは `getPlain` / `firstPlain` は後回し候補とする。SurrealDB の戻り値は object 中心で列定義が RDB より弱く、列順の意味が薄いため、まずは既存の `JsonNode` 中心 API を優先する。 + +### 3. 実装モデル + +prepared statement 実行時は、毎回次の 3 段階で 1 リクエストを組み立てる。 + +1. 元 SQL の `?` を `$a`, `$b`, ... に変換する +2. 各引数を `LET $a = ...; LET $b = ...;` に変換する +3. `LET` 群と本体 SQL を `;` で連結して `/sql` に 1 回だけ POST する + +概念的には次の形になる。 + +```sql +LET $a = "user:alice"; +LET $b = "2026-04-03T00:00:00Z"; +SELECT * FROM user WHERE id = $a AND created_at >= $b; +``` + +この方式なら、すでに `surreal_lib.nim` にある `questionToDaller` と `dbFormat(queryString, args: JsonNode)` の思想をそのまま再利用できる。prepared statement 専用に別の埋め込み規則を増やす必要はない。 + +### 4. 型設計 + +既存 driver に合わせ、プール単位 cache を持つ。 + +```nim +type SurrealPreparedEntry = ref object + sql*: string + normalizedSql*: string + nArgs*: int + refCount*: int + lastUsedAt*: int64 + +type Connections* = ref object + conns*: seq[Connection] + timeout*: int + waiters*: Deque[Future[void]] + preparedCache*: Table[string, SurrealPreparedEntry] + +type SurrealPreparedContext* = ref object + owner*: SurrealConnections + connI*: int + +type SurrealPreparedStatement* = ref object + owner*: SurrealConnections + entry*: SurrealPreparedEntry + sql*: string + isClosed*: bool +``` + +ポイント: + +- Surreal は HTTP `/sql` なので physical statement handle は持たない +- cache の目的は server resource 再利用ではなく、template 正規化と API 一貫性の維持 +- `close()` は logical close とし、cache entry 自体は `flushStmt` / `clearStmtCache` まで残す + +### 5. 実行結果の扱い + +`/sql` の戻り値は statement ごとの配列なので、prepared statement では「最後の本体 SQL の結果」を利用者へ返す。 + +- `get`: 最終 statement の `result` を `seq[JsonNode]` で返す +- `first`: `get` の先頭 1 件を `Option[JsonNode]` で返す +- `exec`: 最終 statement の status を確認し、副作用系として扱う +- `LET` statement の result は利用者に見せない + +ただし error 判定は最後の statement だけでなく全 statement を走査する。`LET` で失敗した場合でも早期に `DbError` を返す必要がある。 + +### 6. `withConn` を残す理由 + +HTTP `/sql` だけを見ると、prepared statement は connection 固定が必須ではない。しかし API 統一のため、`withConn` は残す価値がある。 + +- 既存 driver と同じ呼び出しパターンを維持できる +- SurrealDB の parameter は connection/session scope を持ちうるため、将来 RPC 対応や session 前提最適化へ進む余地を残せる +- 現行の connection pool 制御と整合する + +ただし初期実装では、`withConn` の主目的は性能よりも API 一貫性になる。 + +### 7. multi-statement 利用の境界 + +このブランチで使う multi-statement は「`LET` 群 + 本体 SQL」を 1 リクエストに束ねる用途を主眼とする。 + +- `prepare("UPDATE ...")` は 1 つの本体 statement を持つ +- `LET` は前置 statement +- 複数の業務 statement を 1 個の prepared statement に詰め込む設計は初期スコープ外 + +理由: + +- 既存 prepared statement API も基本は 1 SQL を対象にしている +- SurrealDB では複数 statement が自動で 1 transaction にはならない +- 2 個以上の副作用文を 1 本の prepared statement に許すと、`exec()` の返り値・失敗時意味論・テストが急に複雑になる + +もし将来 multi-statement business query を扱うなら、別 API として `prepareBatch` あるいは transaction helper を検討する。 + +### 8. 実装結果 + +- `surreal_types.nim` に prepared 関連型を追加し、`Connections` に `preparedCache` を持たせた +- `surreal_lib.nim` に prepared SQL 生成の共通関数を追加した +- `surreal_exec.nim` に `prepare` / `get` / `first` / `exec` / `close` / `flushStmt` / `clearStmtCache` / `withConn` を追加した +- `tests/surrealdb/test_prepared_statement.nim` を追加した +- `documents/surrealdb/prepared_statement.md` と既存ドキュメントを同期した + +### 9. テスト方針 + +- `prepare("SELECT ... WHERE id = ?")` で `get` / `first` が通ること +- `prepare("UPDATE ... SET name = ? WHERE id = ?")` で `exec` が通ること +- `close()` 後に同じ SQL を `prepare()` し直しても再利用できること +- `clearStmtCache()` 後に再度 `prepare()` できること +- `withConn` 内の `ctx` overload が通ること +- `JsonNode` 引数で `JInt` / `JString` / `JNull` / `JArray` / `JObject` が `LET` 経由で正しく扱えること +- record id や datetime 文字列の変換が既存 `dbFormat` 規則と一致すること + +### 10. 現時点の結論 + +SurrealDB v3 版 prepared statement は「server-side prepare の移植」ではなく、「既存 prepared statement API と揃った公開インターフェースを、SurrealDB の `LET` と `/sql` multi-statement で安全に再現する」方針が最も自然である。 + +この方針なら `SYW-ARCH-001` に従って公開 API を共通化しつつ、SurrealDB 固有差分は `libs/surreal` と `models/surreal` に閉じ込められる。 diff --git a/clients/nim-allographer/.cursor/rules/branch/340-aged-connection.mdc b/clients/nim-allographer/.cursor/rules/branch/340-aged-connection.mdc new file mode 100644 index 0000000..9d19e17 --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/340-aged-connection.mdc @@ -0,0 +1,271 @@ +--- +description: 340-aged-connectionブランチでの開発時に読み込む +alwaysApply: false +--- +340-aged-connection コネクションプール改善調査 +=== + +このブランチで実装することは以下の通りです。 +- 既存の SQLite / PostgreSQL / MySQL / MariaDB のコネクションプール実装を調査し、長寿命化した接続や壊れた接続を再利用し続けるリスクを洗い出す +- https://please-sleep.cou929.nu/go-sql-db-connection-pool.html の `sql.DB` の考え方を参考に、allographer のプールへ取り込める改善点を整理する +- 「使えないコネクションは破棄してから張り直す」方針を、公開 API を壊さず driver 内へ閉じ込めて実装できる設計に落とし込む +- prepared statement cache / columnTypeCache / transaction 中の接続固定など、再接続時に影響する内部状態の整合条件を明確化する +- 調査結果を踏まえて、各 driver の pool slot を差し替える実装へ進める + +## 進捗 +- [x] `.cursor/rules/project.mdc` / `.cursor/rules/branch.mdc` を確認した +- [x] 現在のブランチ名 `340-aged-connection` に対応するブランチルールを新規作成した +- [x] `src/allographer/query_builder/models/*/*_open.nim` を確認し、各 driver の接続生成方式を調査した +- [x] `src/allographer/query_builder/models/*/*_exec.nim` を確認し、接続取得・返却・待機方式を調査した +- [x] `src/allographer/query_builder/libs/*/*_impl.nim` を確認し、接続異常時の現在の振る舞いを調査した +- [x] 参考記事の `sql.DB` の設計要点を読み、allographer へ適用できる論点を整理した +- [x] 各 driver 共通で使える `ConnectionHealth` / `ConnectionFactory` 相当の内部構造を設計し、`Connections` に lifetime / factory 情報を持たせた +- [x] 接続借用時の寿命判定・再生成手順を driver ごとに実装した +- [x] prepared statement cache の slot 単位無効化を実装し、transaction 中は既存の固定接続をそのまま使う方針を維持した +- [x] 影響範囲に応じた回帰テストを追加した + +## 参考資料 +- 参考記事 + - https://please-sleep.cou929.nu/go-sql-db-connection-pool.html +- 現在の接続初期化 + - `src/allographer/query_builder/models/postgres/postgres_open.nim` + - `src/allographer/query_builder/models/sqlite/sqlite_open.nim` + - `src/allographer/query_builder/models/mysql/mysql_open.nim` + - `src/allographer/query_builder/models/mariadb/mariadb_open.nim` +- 現在の接続プール管理 + - `src/allographer/query_builder/models/postgres/postgres_exec.nim` + - `src/allographer/query_builder/models/sqlite/sqlite_exec.nim` + - `src/allographer/query_builder/models/mysql/mysql_exec.nim` + - `src/allographer/query_builder/models/mariadb/mariadb_exec.nim` +- 現在の型定義 + - `src/allographer/query_builder/models/postgres/postgres_types.nim` + - `src/allographer/query_builder/models/sqlite/sqlite_types.nim` + - `src/allographer/query_builder/models/mysql/mysql_types.nim` + - `src/allographer/query_builder/models/mariadb/mariadb_types.nim` +- 接続利用時の低レベル実装 + - `src/allographer/query_builder/libs/postgres/postgres_impl.nim` + - `src/allographer/query_builder/libs/sqlite/sqlite_impl.nim` + - `src/allographer/query_builder/libs/mysql/mysql_impl.nim` + - `src/allographer/query_builder/libs/mariadb/mariadb_impl.nim` + +## 調査結果・設計まとめ + +### 1. 現在の実装で見えたこと + +allographer のプールは、全 driver とも `dbOpen` 時に `maxConnections` 本を先に開いて配列へ保持する方式になっている。 + +- PostgreSQL: `postgres_open.nim` で全接続を eager に生成し、`createdAt` を持つが、寿命判定にはまだ使っていない +- SQLite: `sqlite_open.nim` でも同様に eager open で、`createdAt` は保持しているが再接続用途には未使用 +- MySQL: `mysql_open.nim` で eager open。`ConnectionInfo` はあるため再接続情報は保持しやすい +- MariaDB: `mariadb_open.nim` も eager open。`ConnectionInfo` はある + +接続取得は、PostgreSQL / SQLite / MariaDB は waiter キューで通知ベース、MySQL はまだ `sleepAsync(10)` ポーリングで待っている。 + +一方で、どの driver でも「借り出す前にその接続が寿命切れか」「壊れていないか」を判定して、不要なら捨てて張り直す仕組みはまだない。 + +### 2. 参考記事から取り込める設計要点 + +参考記事の `sql.DB` で特に重要だった論点は次の 5 点。 + +1. idle 接続と利用中接続を分けて扱う +2. 接続取得時に idle 接続を再利用し、必要なら新規接続する +3. 待ち行列を持ち、上限到達時は空き通知で待つ +4. `SetConnMaxLifetime` / `SetConnMaxIdleTime` 相当で、古い接続を使い回しすぎない +5. 使えない接続は `ErrBadConn` 相当として捨て、再試行できるようにする + +allographer は 1. と 3. は一部できているが、4. と 5. が弱い。今回のブランチの主眼はここに置くのが自然。 + +### 3. 今のプールで改善余地が大きい箇所 + +#### 3.1 aged connection を判定する仕組みがない + +各 driver の `Connection` には `createdAt` があるが、現在は接続寿命の上限判定に使っていない。 + +そのため、 + +- DB サーバ側 `wait_timeout` +- TCP 切断 +- 長時間放置された idle 接続 +- LB / proxy 配下でのコネクション寿命超過 + +といった要因で「見た目はプール内にあるが、実際にはもう使えない接続」を抱え続ける可能性がある。 + +特に MySQL は `mysql_impl.nim` の各所で `assert conn.ping == 0` を前提にしており、壊れた接続に対して graceful に「捨てて張り直す」制御へ入る前に異常化しやすい。 + +#### 3.2 bad connection を pool slot 単位で交換できない + +現状は `self.pools.conns[i].conn` をそのまま実行系へ渡しており、途中で「この接続は再利用不可」と判断しても、その slot の中身を閉じて新しい接続に差し替える共通処理がない。 + +必要なのは次のような流れである。 + +1. 接続借用前に寿命・健全性を判定する +2. 異常ならその slot の接続を close する +3. 同じ slot に新しい接続を張り直す +4. その slot にぶら下がる prepared statement 状態を無効化する +5. その後でクエリ本体へ進む + +今は 2. から 4. の共通導線が存在しない。 + +#### 3.3 reconnect に必要な情報が driver ごとに不揃い + +MySQL / MariaDB は `ConnectionInfo` を持つが、PostgreSQL / SQLite の `Connections` には再接続用の接続情報が保存されていない。 + +したがって共通の「壊れた slot を再接続する」設計を入れるには、少なくとも内部的には各 driver の `Connections` に factory 情報を保持させる必要がある。 + +例: + +- PostgreSQL: database / user / password / host / port / non-blocking 設定 +- SQLite: database path +- MySQL / MariaDB: 既存 `ConnectionInfo` を `Connections` から参照できる形へ統一 + +#### 3.4 prepared statement cache が再接続で壊れうる + +PostgreSQL / MySQL / MariaDB / SQLite は SQL 単位 cache を持ちつつ、物理 statement handle は conn index ごとに保持している。 + +このため slot `i` の接続を捨てて張り直すなら、少なくとも slot `i` に紐づく prepared 情報はクリアしないといけない。 + +- PostgreSQL: `stmtNames[i]` を空に戻す必要がある +- MySQL / MariaDB: `stmts[i]` を close して nil に戻す必要がある +- SQLite: `stmts[i]` を finalize して nil に戻す必要がある + +再接続だけして cache を残すと、「古い接続に prepare されたハンドルを新しい接続で再利用しようとする」不整合になる。 + +#### 3.5 transaction 中は自動再接続の扱いを厳格に分ける必要がある + +通常クエリでは bad connection を破棄して retry できる余地があるが、transaction 中は話が変わる。 + +- `BEGIN` 後の接続が死んだら、その transaction 文脈は失われる +- 同じ API 呼び出しを別接続へ載せ替えても、意味上は同じ transaction ではない + +したがって transaction 中は、 + +- 自動再接続して継続してよい場面 +- 明示的に `DbError` を返して transaction abort を促す場面 + +を分ける必要がある。初期方針としては「transaction 中は bad connection を検知したら再接続はしても自動リトライしない」が安全。 + +### 4. このブランチで採るべき基本方針 + +#### 方針A: pool slot を生かし、接続だけ差し替える + +公開 API を崩さないため、接続プール自体を大きく作り変えるより、既存の `conns[i]` 配列を維持したまま「slot 内の接続実体だけ交換する」方がよい。 + +これにより、 + +- `getFreeConn` / `returnConn` の外形は維持できる +- prepared cache の添字も維持できる +- driver 差分を `*_open.nim` / `*_exec.nim` / `*_types.nim` に閉じ込めやすい + +#### 方針B: 借用時に寿命判定、実行失敗時に bad connection 判定を入れる + +初回実装は、次の 2 箇所で接続再生成を行うのがよい。 + +1. `getFreeConn` で idle 接続を貸し出す直前 +2. クエリ / prepare 実行中に「接続断」と判断できる例外を受けた直後 + +1 は proactive、2 は reactive な対策である。両方あると「古い接続をなるべく使わない」と「想定外に死んだ接続をすぐ交換する」の両立ができる。 + +#### 方針C: ping 常用よりも age/lifetime と error 分類を優先する + +毎回 ping してから貸し出す設計は単純だが、MySQL/MariaDB では同期的な `ping` が待機コストや event loop 停止要因になりうる。 + +そのため初手は、 + +- `createdAt` ベースの `maxLifetime` +- 必要なら `lastUsedAt` ベースの `maxIdleTime` +- driver 固有の bad connection エラー分類 + +を優先し、health check は「寿命超過時のみ」または「失敗後の再接続確認時のみ」に寄せるほうがよい。 + +### 5. 具体的な内部設計案 + +#### 5.1 `Connections` に再接続用メタデータを持たせる + +各 driver の `Connections` に、少なくとも次を持たせる。 + +- 接続生成に必要な情報 +- `maxLifetimeSeconds` +- `maxIdleSeconds` +- `lastUsedAt` または `returnedAt` +- 必要なら `isBroken` + +既存 `createdAt` はそのまま使う。 + +#### 5.2 `replaceConn(i)` 相当の内部 proc を driver ごとに用意する + +責務: + +1. slot `i` の古い接続を close +2. slot `i` に紐づく prepared state を破棄 +3. 新しい物理接続を開く +4. `createdAt` / `isBusy` / `lastUsedAt` を初期化 + +この proc は driver ごとの差分が大きいので、共通化しすぎず各 driver 実装へ置く。 + +#### 5.3 `getFreeConn` に「借り出し前の再生成」フックを入れる + +通知ベース待機の有無に関係なく、空き slot を見つけたタイミングで次を行う。 + +1. `isBusy = true` +2. `shouldRecycle(conn)` を判定 +3. true なら `replaceConn(i)` +4. 成功した slot を返す + +ここで失敗した場合は、その slot を unusable のまま返さず、別 slot 探索または例外へ進める。 + +#### 5.4 実行ラッパで bad connection を捕捉して slot を再初期化する + +`query`, `exec`, `prepare` など接続を直接使う箇所は多いので、driver ごとに + +- `withBorrowedConn` +- `runWithConnRecovery` + +のような内部ラッパへ寄せる余地がある。 + +このラッパは、 + +1. 実行 +2. bad connection 例外なら slot を破棄 +3. 非 transaction 時のみ 1 回だけ再接続して再試行 + +を担当する。 + +### 6. 影響の大きさから見た優先順位 + +優先度 P0: +- MySQL / MariaDB / PostgreSQL / SQLite で「壊れた接続を破棄して張り直す」最小導線を作る +- prepared statement の conn index 単位 invalidation を入れる +- transaction 中は自動リトライしない + +優先度 P1: +- `maxLifetime` を導入して aged connection の再利用を減らす +- `maxIdleTime` を導入する +- MySQL の pool wait を通知ベースへ寄せる + +優先度 P2: +- idle cleaner の別タスク化 +- lazy open 化 +- 統計情報の公開 + +今回のブランチ名と依頼内容からすると、まずは P0 と P1 の前半までを対象にするのが適切。 + +### 7. 現時点の結論 + +現在のコネクションプールで最も改善効果が大きいのは、「接続をただ保持する」のではなく、「貸し出し時と失敗時に、その接続がまだ使えるかを判断し、ダメなら slot ごと再初期化する」層を追加することだと判断した。 + +参考記事の `sql.DB` のうち、allographer にいま不足しているのは主に次の部分である。 + +- 接続寿命管理 +- bad connection の廃棄と再試行 +- idle connection の掃除 + +### 8. 実装後の方針 + +- pool ごとに `maxConnectionLifetime` / `maxConnectionIdleTime` を持たせ、既定値は 300 秒にした +- 借用時に `createdAt` / `lastUsedAt` を見て aged connection を `refreshConn` で差し替える +- 差し替え時は prepared statement cache を slot 単位で無効化し、古い statement handle を新しい physical connection に持ち込まない +- transaction 中は既存の接続固定を優先し、途中で別 slot に載せ替える振る舞いは入れない + +特に今回の要望である「コネクションが使えない時は破棄してから、新しくコネクションを張り直す」は、driver ごとの `replaceConn(i)` と、prepared cache invalidation を軸に設計するのが最も自然である。 + +一方で transaction 中の自動再接続は意味論を壊しやすいため、通常クエリと同列には扱わない。ここは安全側に倒し、まずは「通常クエリのみ 1 回まで回復可能、transaction 中は即エラー」を初期設計とする。 diff --git a/clients/nim-allographer/.cursor/rules/branch/fix.mdc b/clients/nim-allographer/.cursor/rules/branch/fix.mdc new file mode 100644 index 0000000..533f2f0 --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/branch/fix.mdc @@ -0,0 +1,35 @@ +--- +description: fixブランチでの開発時に読み込む +alwaysApply: false +--- +fix ブランチルール +=== + + +allographer自体の改善 + +- `getFreeConn` がタイムアウトすると `errorConnectionNum` を返すが、その後の `getRowPlain` / `exec` / `getAllRows` は例外ではなくそのまま `return` している。結果として、更新できていなくてもアプリ側では成功レスポンスを返せてしまう。 +- prepared statement は接続ごとに lazy prepare されるが、プール単位の cache がない。高頻度 SQL が複数接続に散ると、prepare の局所性が弱い。 +- `close()` は使った接続全てに `DEALLOCATE` を送る実装なので、短寿命の prepared statement を多用すると warm 状態を毎回捨てる。 +- DML 実行時の列型取得は現在は table 単位 cache になっているが、bulk update 用の raw SQL / prepared SQL を多用する場合は、そもそも query builder を通さない高速経路の価値が高い。 + +改善方針: +- `errorConnectionNum` に到達したときは no-op ではなく `DbError` を投げる。silent failure は benchmark だけでなく通常アプリでも危険。 +- `Connections` に SQL 単位の prepared cache を持たせ、同一 SQL の physical prepare を接続ごとに 1 回へ寄せる。 +- `close()` は論理 close に寄せ、物理 `DEALLOCATE` は明示 API に分離する。steady-state の benchmark では prepared state を維持できるようにする。 +- 接続固定 API、あるいは transaction / prepared context を足し、同一 logical operation の複数クエリを同じ接続に寄せられるようにする。 +- bulk update 用に `raw(...).exec()` だけでなく、prepared raw SQL を再利用できる経路を整える。今回のベンチマーク用途では query builder の汎用性よりも固定 SQL の再利用性が効く。 + +## 進捗 +- [x] `errorConnectionNum` 到達時の silent return を `DbError` へ置換(PostgreSQL / MySQL / MariaDB / SQLite) +- [x] PostgreSQL に SQL 単位の prepared cache (`Connections.preparedCache`) を追加 +- [x] PostgreSQL prepared `close()` を論理 close 化し、`flushStmt(sql)` / `clearStmtCache()` を追加 +- [x] PostgreSQL に `withConn` / `PostgresPreparedContext` を追加し、prepared `first/get/exec` の `ctx` overload を追加 +- [x] `example/benchmark.nim` を prepared cold/warm 計測へ分離し、warm 側で statement 再利用経路を追加 +- [x] PostgreSQL prepared statement テストに論理 close / cache clear / prepared context ケースを追加 +- [x] pool wait テストに pool size 0 時の `DbError` 検証を追加(PostgreSQL / MariaDB / SQLite) + +## 調査結果・設計まとめ +- `errorConnectionNum` の no-op return は、DML 失敗を呼び出し側が検知できないため、全 RDB driver で同じ失敗モードを発生させていた。今回 `DbError` へ統一し、失敗を上位へ伝播させる。 +- prepared cache はまず PostgreSQL に導入した。理由は `PREPARE/DEALLOCATE` のネットワーク往復コストがベンチで支配的だったため。 +- `close()` の意味を「ハンドルの寿命」と「物理 prepared state の寿命」で分離し、warm 計測で state を保持できるようにした。 diff --git a/clients/nim-allographer/.cursor/rules/project.mdc b/clients/nim-allographer/.cursor/rules/project.mdc new file mode 100644 index 0000000..af22549 --- /dev/null +++ b/clients/nim-allographer/.cursor/rules/project.mdc @@ -0,0 +1,201 @@ +--- +alwaysApply: true +--- +プロジェクトルール +=== + +## 0. 規範表現(RFC 2119)と Lint方針 + +このドキュメントの規範キーワードは以下の意味で扱う。 +- **MUST**: 必須。違反はレビューで差し戻し対象 +- **SHOULD**: 強い推奨。逸脱時は理由を記録する +- **MAY**: 任意。採用時は周辺整合性を優先する +- **MUST NOT**: 禁止。明示的な合意なしに導入してはならない + +各ルールには Rule ID を付与する。 +- 形式: `SYW--` +- 併記形式: `[ID][LEVEL][CHECK]` +- `CHECK` の意味: + - `grep`: 文字列や配置で確認しやすい + - `manual`: レビューで確認する + - `test`: テストで確認する + - `doc`: 文書整合の確認 + +本文とルール一覧が矛盾した場合は、ルール一覧を優先する。 + +### 0.1 ルール一覧(要約) + +| ID | LEVEL | CHECK | 対象 | 要件(要約) | +|---|---|---|---|---| +| SYW-GOAL-001 | MUST | manual | 全体 | 本リポジトリは Nim による RDB 向け query builder / migration / seeder を中核成果物とする | +| SYW-GOAL-002 | MUST | manual | API | Laravel Query Builder / Orator に学んだ直感的で連鎖可能な API を保つ | +| SYW-ARCH-001 | MUST | manual | 設計 | DB差分は driver 実装へ閉じ込め、公開 API は可能な限り共通化する | +| SYW-ARCH-002 | MUST NOT | manual | 設計 | 一時的な場当たり分岐を公開 API に持ち込まない | +| SYW-QB-001 | MUST | test | Query Builder | SQL 生成は決定的で、同じ入力に対し同じクエリ文字列・束縛順序を返す | +| SYW-QB-002 | MUST | manual | Query Builder | メソッドチェーンは可読性を優先し、Laravel/Orator 利用者が推測可能な命名を採用する | +| SYW-QB-003 | MUST NOT | manual | Query Builder | RDB 向け API に暗黙の破壊的副作用を持たせない | +| SYW-SCHEMA-001 | MUST | test | Migration | migration は冪等に再実行判定でき、履歴テーブルと整合する | +| SYW-SCHEMA-002 | MUST | test | Schema Builder | create / alter / drop は DB ごとの差異を吸収しつつ意味論を維持する | +| SYW-SEED-001 | MUST | test | Seeder | seeder は空判定または条件判定に基づき安全に再実行できる | +| SYW-COMPAT-001 | MUST | manual | 互換性 | 既存公開 API の破壊的変更は、必要性・移行方針・影響範囲を文書化する | +| SYW-TEST-001 | MUST | test | テスト | 修正した機能レイヤに対応するテストを追加または更新する | +| SYW-TEST-002 | SHOULD | test | テスト | DB 横断の共通仕様は複数 driver で検証する | +| SYW-DOC-001 | MUST | doc | ドキュメント | README / documents / ルール文書は、公開仕様変更時に同期する | +| SYW-SCOPE-001 | MUST NOT | manual | スコープ | ORM 全面実装を前提にせず、query builder・schema builder の完成度を優先する | + +## 1. 目的・スコープ + +### 1.1 達成目標 + +[SYW-GOAL-001][MUST][manual] +このプロジェクトの達成目標は、**Nim 言語で Laravel の Query Builder / ORM 的な記述感と Python Orator の使いやすさを参考にした、RDB 向け query builder を実装・改善・保守すること**である。 + +[SYW-GOAL-002][MUST][manual] +中核機能は以下の 3 系統である。 +- Query Builder +- Migration / Schema Builder +- Seeder + +[SYW-GOAL-003][MUST][manual] +利用者は「生 SQL を毎回手で組み立てなくても、読みやすいメソッドチェーンで安全に RDB を扱える」ことを期待している。実装判断は常にこの期待を優先する。 + +### 1.2 主要スコープ + +[SYW-SCOPE-001][MUST][manual] +主要スコープは以下である。 +- SQLite / PostgreSQL / MySQL / MariaDB などの RDB 向け query builder +- create / alter / drop を扱う schema builder +- migration 履歴管理 +- 初期データ投入用 seeder +- 非同期実行を前提とした接続・実行モデル + +[SYW-SCOPE-002][SHOULD][manual] +既存コードベースに SurrealDB 実装が存在していても、判断に迷った場合はまず RDB 向け API 品質を優先する。 + +### 1.3 非スコープ + +[SYW-SCOPE-003][MUST NOT][manual] +以下は明示的な要件がない限り優先しない。 +- Active Record 的な重い ORM 機能の全面拡張 +- マジックに依存した暗黙変換の追加 +- driver ごとに大きく振る舞いが異なる独自 API の増殖 + +## 2. 設計原則 + +### 2.1 API 原則 + +[SYW-ARCH-001][MUST][manual] +公開 API は driver 差分より先に、利用者の記述体験を基準に設計する。各 driver の都合は内部実装で吸収する。 + +[SYW-QB-002][MUST][manual] +メソッド名・引数順・返り値は、Laravel Query Builder / Orator を知る利用者が高い確率で推測できる形を優先する。 + +[SYW-QB-003][MUST NOT][manual] +`select`, `where`, `join`, `insert`, `update`, `delete`, `first`, `find`, `get` などの基本操作に、利用者が予期しない破壊的副作用を持ち込んではならない。 + +[SYW-COMPAT-001][MUST][manual] +公開 API の破壊的変更を行う場合は、少なくとも以下を変更箇所に残す。 +- 何が壊れるか +- なぜ必要か +- 既存利用者の移行方法 + +### 2.2 実装分離 + +[SYW-ARCH-002][MUST NOT][manual] +「特定 driver だけ通すための場当たり的 if 文」を上位 API 層へ散在させてはならない。分岐は generator / lib / model など責務のある層へ閉じ込める。 + +[SYW-ARCH-003][MUST][manual] +責務分離の目安は以下とする。 +- 接続管理: `connection` 系 +- API 入口と共通操作: `query_builder` / `schema_builder` +- DB 方言差分: `models/*/query/*`, `libs/*` +- 実行・変換: `*_exec`, `*_types`, `*_query` + +[SYW-ARCH-004][SHOULD][manual] +新機能は、まず共通仕様を言語化してから driver 差分へ展開する。最初に 1 driver だけ特殊実装へ逃がさない。 + +## 3. Query Builder ルール + +[SYW-QB-001][MUST][test] +同じ builder 入力は、同じ SQL 文字列と同じ bind 順序を生成しなければならない。出力が非決定的になる変更は禁止する。 + +[SYW-QB-004][MUST][manual] +where 句、join、group by、order by、limit / offset、paginate などの句の結合順は、利用者の記述順と SQL としての自然順を壊さない。 + +[SYW-QB-005][SHOULD][manual] +値の埋め込みよりも、既存のプレースホルダ・エスケープ・型変換の仕組みを優先して使う。 + +[SYW-QB-006][MUST][test] +`first` / `find` / `get` の空結果時の振る舞いは既存仕様を維持する。`Option` や空シーケンスの扱いを無言で変えてはならない。 + +[SYW-QB-007][SHOULD][manual] +ログ出力やデバッグ補助を追加する場合は、通常経路の性能や API 純度を汚さない。 + +## 4. Migration / Schema Builder ルール + +[SYW-SCHEMA-001][MUST][test] +migration は履歴テーブルにより再実行可否を判定できなければならない。二重適用や履歴不整合を起こす変更は禁止する。 + +[SYW-SCHEMA-002][MUST][test] +`create`, `alter`, `drop`, `rename`, `add column`, `change column`, `drop column` は、DB ごとの差異があっても利用者にとって一貫した意味を維持する。 + +[SYW-SCHEMA-003][MUST][manual] +破壊的な schema 操作を追加・変更する場合は、ロールバック不能性やデータ移行影響を必ず確認する。 + +[SYW-SCHEMA-004][SHOULD][manual] +column 定義 API は Laravel ライクな表現を優先し、命名やオプションの意味が直感に反しないようにする。 + +## 5. Seeder ルール + +[SYW-SEED-001][MUST][test] +seeder は再実行時に重複データを無制限に増やさないよう、空テーブル判定または列条件判定を用いる既存思想を維持する。 + +[SYW-SEED-002][SHOULD][manual] +サンプル seeder と実装が乖離した場合は、利用者が最初に触れる example を優先して同期する。 + +## 6. テスト・品質保証 + +[SYW-TEST-001][MUST][test] +コード変更時は、影響する最小レイヤのテストを追加または更新する。 +- query 生成を変えたら query テスト +- schema 操作を変えたら schema / create_schema テスト +- トランザクションを変えたら transaction テスト +- utility を変えたら utils テスト + +[SYW-TEST-002][SHOULD][test] +共通 API の変更は、可能な限り SQLite だけでなく PostgreSQL / MySQL / MariaDB 系にも同種の検証を広げる。 + +[SYW-TEST-003][MUST][manual] +テスト追加が難しい場合は、その理由と未検証リスクを作業記録へ残す。 + +## 7. ドキュメント・例・後方互換 + +[SYW-DOC-001][MUST][doc] +公開仕様を変えたら、必要に応じて以下を同期する。 +- `README.md` +- `documents/rdb/*.md` +- `example/` +- `.cursor/rules/*.mdc` + +[SYW-DOC-002][SHOULD][doc] +サンプルコードは「最短で使い方が伝わる」ことを優先し、実装とズレた古い記法を放置しない。 + +## 8. 実装判断の優先順位 + +[SYW-PRIO-001][MUST][manual] +判断に迷った場合の優先順位は以下とする。 +1. 利用者にとって API が自然か +2. 既存公開仕様を壊さないか +3. 複数 RDB で意味が揃うか +4. 内部実装が責務分離されているか +5. テストと文書で保守できるか + +## 9. このプロジェクトで避けること + +[SYW-ANTI-001][MUST NOT][manual] +以下は避ける。 +- 一部 driver だけを通すための公開 API 汚染 +- テストなしの SQL 生成変更 +- migration 履歴や seeder 冪等性を壊す変更 +- README と実装の乖離放置 +- Nim らしさよりも複雑なマジックを優先すること diff --git a/clients/nim-allographer/.env b/clients/nim-allographer/.env new file mode 100644 index 0000000..bf44f76 --- /dev/null +++ b/clients/nim-allographer/.env @@ -0,0 +1,17 @@ +SQLITE_HOST="/root/db.sqlite3" +# SQLITE_HOST=":memory:" +PG_URL="postgresql://user:pass@postgres:5432/database" +MARIA_URL="mariadb://user:pass@mariadb:3306/database" +MY_URL="mysql://user:pass@mysql:3306/database" +SURREAL_HOST="http://surreal" +SURREAL_PORT=8000 +DB_USER=user +DB_PASSWORD=pass +DB_DATABASE=database +DB_MAX_CONNECTION=20 +DB_TIMEOUT=30 + +# Logging +LOG_IS_DISPLAY=true +LOG_IS_FILE=false +LOG_DIR="/root/project/logs" diff --git a/clients/nim-allographer/.github/pr-labeler.yml b/clients/nim-allographer/.github/pr-labeler.yml new file mode 100644 index 0000000..be0dbe0 --- /dev/null +++ b/clients/nim-allographer/.github/pr-labeler.yml @@ -0,0 +1,3 @@ +feature: feature* +bug: hotfix* +chore: chore* diff --git a/clients/nim-allographer/.github/release-drafter.yml b/clients/nim-allographer/.github/release-drafter.yml new file mode 100644 index 0000000..5a8946e --- /dev/null +++ b/clients/nim-allographer/.github/release-drafter.yml @@ -0,0 +1,19 @@ +name-template: 'v$NEXT_PATCH_VERSION' +tag-template: 'v$NEXT_PATCH_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +template: | + ## Changes + + $CHANGES diff --git a/clients/nim-allographer/.github/workflows/docs.yml.bk b/clients/nim-allographer/.github/workflows/docs.yml.bk new file mode 100644 index 0000000..71fc797 --- /dev/null +++ b/clients/nim-allographer/.github/workflows/docs.yml.bk @@ -0,0 +1,33 @@ +name: Deploy API documents + +on: + push: + branches: + - main + +jobs: + build-docs-deploy: + env: + NIM_VERSION: 1.4.4 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Cache choosenim + id: cache-choosenim + uses: actions/cache@v1 + with: + path: ~/.choosenim + key: ${{ runner.os }}-choosenim-${{ env.NIM_VERSION }} + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: ${{ env.NIM_VERSION }} + - name: Build + run: nimble install -Y + - name: Generate documents + run: nimble docs + - name: Deploy + uses: peaceiris/actions-gh-pages@v2.4.0 + env: + ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }} + PUBLISH_BRANCH: gh-pages + PUBLISH_DIR: ./docs diff --git a/clients/nim-allographer/.github/workflows/nim.yml b/clients/nim-allographer/.github/workflows/nim.yml new file mode 100644 index 0000000..7256c88 --- /dev/null +++ b/clients/nim-allographer/.github/workflows/nim.yml @@ -0,0 +1,61 @@ +name: Build and test Nim + +# テストはPRだけ +on: + pull_request: + paths-ignore: + - '*.md' + - 'documents/*' + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "test" + cancel-in-progress: false + +jobs: + # WIP でジョブがスキップされてもCIが失敗した扱いにならないようにするため + skip: + runs-on: ubuntu-latest + steps: + - run: echo "Skip job" + + # WIP がコミットメッセージに含まれているとジョブを起動しない + before: + runs-on: ubuntu-latest + if: "! contains(github.event.head_commit.message, 'WIP')" + steps: + - run: echo "no WIP" + + test-on-docker-nim: + runs-on: ubuntu-latest + needs: before + strategy: + fail-fast: false + matrix: + nim-version: + - 2.0.16 + - 2.2.8 + env: + NIM_VERSION: ${{ matrix.nim-version }} + steps: + - uses: actions/checkout@v2 + - name: Build docker image + run: docker compose -f compose.test.yaml build app + - name: Start dependencies + run: docker compose -f compose.test.yaml up -d postgres mysql mariadb surreal + - name: Test + run: docker compose -f compose.test.yaml run --rm --no-deps app nimble test + + + # アプリケーションの方を使うことにした + # https://github.com/marketplace/actions/release-drafter + # create-tag-draft: + # runs-on: ubuntu-latest + # if: github.ref == 'refs/heads/master' + # needs: + # - test-on-docker + # steps: + # - uses: release-drafter/release-drafter@v5.3.1 + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/clients/nim-allographer/.github/workflows/pr-labeler.yml b/clients/nim-allographer/.github/workflows/pr-labeler.yml new file mode 100644 index 0000000..5f15227 --- /dev/null +++ b/clients/nim-allographer/.github/workflows/pr-labeler.yml @@ -0,0 +1,15 @@ +name: labeler + +on: + pull_request: + types: [opened] + +jobs: + labeler: + runs-on: ubuntu-latest + steps: + - uses: TimonVS/pr-labeler-action@v3.1.0 + with: + configuration-path: .github/pr-labeler.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/clients/nim-allographer/.gitignore b/clients/nim-allographer/.gitignore new file mode 100644 index 0000000..137a3c7 --- /dev/null +++ b/clients/nim-allographer/.gitignore @@ -0,0 +1,32 @@ +* +!*.* +!*/ +!makefile + +# Created by https://www.gitignore.io/api/nim +# Edit at https://www.gitignore.io/?templates=nim + +### Nim ### +nimcache/ + +# End of https://www.gitignore.io/api/nim + +!LICENSE +!Dockerfile + +coverage +lcov.info + +*.sqlite3 +*.srqlnb +.migration.json +*logs/* +*.log +.diff +testresults + + +gh-pages* +.DS_Store +pkg +.vscode diff --git a/clients/nim-allographer/AGENTS.md b/clients/nim-allographer/AGENTS.md new file mode 100644 index 0000000..b96ec45 --- /dev/null +++ b/clients/nim-allographer/AGENTS.md @@ -0,0 +1,22 @@ +# AGENTS + +- `.cursor/rules/project.mdc` を必ず読み、プロジェクトルールと達成目標を確認する。 +- `.cursor/rules/branch.mdc` を必ず読み、ブランチルールを確認する。 +- 現在のブランチ名と一致するファイル名のファイルが、リポジトリのルートから見て `.cursor/rules/branch/` 配下にあった場合は、それを読み込んでください。 +- Think harder. +- Deep research. +- If you output anything other than the final code, the answer is invalid. +- git add, git commitのコマンド操作は禁止。 +- 常に日本語で回答する。 +- "commit message"と入力されたら次のことを行う + - `cd {project_root} && git diff --cached > .diff` を実行して.diffファイルを作成する + - .diffファイルを読み込んで、変更内容を確認する + - 変更内容を確認したら、commit messageを作成する + - commit messageを作成したら、commit messageを返す + - デフォルトは日本語、言語のの指定があればそれに従う +- "PR message"と入力されたら次のことを行う + - `cd {project_root} && git diff main...HEAD > .diff` を実行して.diffファイルを作成する + - .diffファイルを読み込んで、変更内容を確認する + - 変更内容を確認したら、PR messageを作成する + - PR messageを作成したら、PR messageを返す + - デフォルトは日本語、言語の指定があればそれに従う \ No newline at end of file diff --git a/clients/nim-allographer/LICENSE b/clients/nim-allographer/LICENSE new file mode 100644 index 0000000..086a9ff --- /dev/null +++ b/clients/nim-allographer/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2019 Hidenobu Itsumura + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/clients/nim-allographer/PLAN_BARADB.md b/clients/nim-allographer/PLAN_BARADB.md new file mode 100644 index 0000000..480a2ac --- /dev/null +++ b/clients/nim-allographer/PLAN_BARADB.md @@ -0,0 +1,353 @@ +# План за интеграция на BaraDB в nim-allographer + +## Цел +**BaraDB** е пълноценен драйвер в `nim-allographer`. Използва се чист Nim клиент (`BaraClient` / `SyncClient`) без C зависимости. Compile-time switch: `DB_BARADB`. + +--- + +## Актуално състояние (2026-05-21) + +### Статус по фази + +| Фаза | Състояние | Бележки | +|------|-----------|---------| +| 1. Инфраструктура и типове | **ГОТОВО** | `env.nim`, `connection.nim`, `query_builder.nim`, `schema_builder.nim` — всичко е интегрирано | +| 2. Query Builder — SQL генерация | **ГОТОВО** | `baradb_generator.nim` (397 реда), `baradb_builder.nim` (237 реда) — всички CRUD + агрегати | +| 3. Query Builder — execution | **ГОТОВО** (с бележки) | `baradb_exec.nim` (716 реда) — всички операции работят, но `insertId` използва `SELECT MAX` вместо `RETURNING` | +| 4. Schema Builder | **СКЕЛЕТ** | Flow-ът е налице, но column type mapping е 24 реда вместо 466 (PostgreSQL) | +| 5. Тестове и документация | **МИНИМАЛНО** | Само `test_open.nim` и `test_query.nim` | + +--- + +## Какво е ГОТОВО (не пипай) + +### Wire Protocol клиент — `query_builder/libs/baradb/baradb_client.nim` (706 реда) +- Pure Nim, async + sync, без C FFI +- Binary serialization (big-endian) на всички `WireValue` типове (13 типа: fkNull..fkJson) +- `mkQuery`, `mkQueryParams`, `mkAuth`, `mkPing` съобщения +- `readQueryResponse` — парсва `mkData`, `mkComplete`, `mkError`, `mkReady` +- Вграден `QueryBuilder` (fluent) за standalone употреба + +### Connection Pool — `baradb_open.nim` + `baradb_exec.nim` +- `dbOpen(Baradb, database, user, pass, host, port, maxConnections, timeout)` +- `getFreeConn()` — async с deque-based waiters и timeout +- `returnConn()` — освобождава и буди чакащи +- Connection aging: `maxConnectionLifetime`, `maxConnectionIdleTime` +- `refreshConn()`, `hasConnExpired()`, `openBaradbConn()` + +### Query Builder — `baradb_query.nim` (386 реда) +- Всички fluent методи: `select`, `table`, `distinct`, `join`, `leftJoin` +- `where` / `orWhere` (string|int|float, bool, nil overloads) +- `whereBetween`, `whereNotBetween`, `whereIn`, `whereNotIn`, `whereNull` +- `groupBy`, `having`, `orderBy`, `limit`, `offset` +- `raw(sql, args)` — суров SQL с параметри + +### SQL Generator — `baradb_generator.nim` (397 реда) +- Всички SQL конструкции: SELECT, INSERT, UPDATE, DELETE, агрегати +- Quoting с backtick `` ` `` (MySQL/SQLite стил, **не** PostgreSQL `"`) +- Placeholder: `?` (не `$1`) + +### Execution — `baradb_exec.nim` (716 реда) +- `get()`, `first()`, `find()` → `seq[JsonNode]` / `Option[JsonNode]` +- `getPlain()`, `firstPlain()`, `findPlain()` → `seq[seq[string]]` +- `insert(JsonNode)`, `insert(seq[JsonNode])`, `insertId(...)` +- `update(JsonNode)`, `delete()`, `delete(id)` +- `count()`, `min()`, `max()`, `avg()`, `sum()` +- `begin()`, `commit()`, `rollback()` + `transaction` макро +- Raw query варианти за всички операции +- `seeder` шаблони + +### ORM — споделен модул +- `orm()` template работи с BaraDB + +### Schema Code Generation — `usecases/baradb/create_schema.nim` +- Четене от `information_schema` и генериране на Nim типове + +--- + +## Какво трябва да се ДОИЗПИПА + +### 1. Prepared Statements — САМО типове, НЯМА имплементация + +**Текущо състояние:** Типовете са дефинирани в `baradb_types.nim`: +- `BaradbPreparedEntry` — `sql`, `nArgs`, `refCount`, `lastUsedAt` +- `BaradbPreparedContext` — `owner`, `connI` +- `BaradbPreparedStatement` — `owner`, `entry`, `sql`, `nArgs`, `isClosed` +- `preparedCache` е инициализиран в `baradb_open.nim` + +**Липсващо:** +- `prepare()` proc — да създава `BaradbPreparedStatement` +- `ensureStmt()` — да кешира и преизползва statement-и +- `get*()` / `exec*()` procs върху `BaradbPreparedStatement` +- `withConn()` proc за context-based изпълнение +- `flushStmt()` / `clearStmtCache()` + +**Wire protocol поддръжка:** `baradb_client.nim` вече има `mkQueryParams` — параметризираните заявки са вградени на ниво протокол. Трябва само да се свърже от allographer слоя. + +**Проблем:** В момента `baradb_exec.nim` използва **client-side string interpolation** (`formatSql` + `escapeSqlValue`) вместо server-side параметри. Това е по-малко сигурно и по-малко ефективно. + +**Референция:** PostgreSQL драйверът има ~300 реда prepared statement код (`postgres_exec.nim` — `ensureStmt`, `preparedQuery`, `preparedExec`, `deallocate`, `flush`, `clearCache`, context API). + +**План:** +``` +Файл: baradb_exec.nim +- [ ] prepare(sql: string): BaradbPreparedStatement +- [ ] ensureStmt(conn, sql, nArgs): BaradbPreparedEntry — кеширане в preparedCache +- [ ] preparedGet(stmt, args): seq[JsonNode] — използва mkQueryParams +- [ ] preparedExec(stmt, args): void +- [ ] withConn(pool, callback): auto — context-based +- [ ] flushStmt(stmt): void — deallocate +- [ ] clearStmtCache(): void +``` + +--- + +### 2. RETURNING id за INSERT — използва SELECT MAX (race condition) + +**Текущо състояние в `baradb_exec.nim`:** +```nim +discard await self.pools.conns[connI].client.exec(sql) +let idSql = &"SELECT MAX(\"{key}\") FROM \"{table}\"" +let qr = await self.pools.conns[connI].client.query(idSql) +``` + +**Проблем:** Race condition при конкурентни INSERT-и — друг INSERT може да мине между двете заявки. + +**Варианти:** +1. **`INSERT ... RETURNING id`** — ако BaraDB го поддържа (за предпочитане, атомарно) +2. **`last_insert_rowid()`** — ако BaraDB има такава функция (както SQLite) +3. **`SELECT LAST_INSERT_ID()`** — MySQL-стил +4. **CTE с `INSERT ... RETURNING`** — `WITH inserted AS (INSERT ...) SELECT id FROM inserted` + +**План:** +``` +Файл: baradb_exec.nim — insertId proc +- [ ] Проверка дали BaraDB поддържа RETURNING +- [ ] Ако да: INSERT ... RETURNING "id" (като PostgreSQL) +- [ ] Ако не: използване на заявката за последно вмъкнат ID от wire protocol-а +- [ ] Премахване на SELECT MAX workaround +``` + +--- + +### 3. Schema Builder — Column Type Mapping е СКЕЛЕТ + +**Текущо състояние в `queries/baradb/sub/create_column_query.nim` (24 реда):** +```nim +query.add(&"`{column.name}` {column.typ}") +# Само: NOT NULL, UNIQUE, DEFAULT +``` + +**Проблем:** Изкарва raw `RdbTypeKind` enum стойности (`rdbIncrements`, `rdbString`) вместо SQL типове (`SERIAL`, `VARCHAR(256)`). + +**Референция:** PostgreSQL `sub/create_column_query.nim` — **466 реда**, обработва всички `RdbTypeKind`: +- `rdbIncrements` → `SERIAL PRIMARY KEY` +- `rdbBigIncrements` → `BIGSERIAL PRIMARY KEY` +- `rdbInteger` → `INTEGER` +- `rdbBigInteger` → `BIGINT` +- `rdbBoolean` → `BOOLEAN` +- `rdbString(n)` → `VARCHAR(n)` (default 255) +- `rdbText` → `TEXT` +- `rdbFloat` → `DOUBLE PRECISION` +- `rdbDecimal(p,s)` → `DECIMAL(p,s)` +- `rdbDate` → `DATE` +- `rdbDateTime` / `rdbTimestamp` → `TIMESTAMP` +- `rdbTimestampz` → `TIMESTAMPTZ` +- `rdbTime` → `TIME` +- `rdbBinary` → `BYTEA` +- `rdbUuid` → `UUID` +- `rdbJson` / `rdbJsonb` → `JSON` / `JSONB` +- `rdbEnum` → `VARCHAR(255)` + CHECK constraint +- Foreign keys: `REFERENCES "table"("column") ON DELETE/UPDATE CASCADE/SET NULL/...` +- Indexes, comments, unsigned + +**План:** +``` +Файл: queries/baradb/sub/create_column_query.nim +- [ ] Пълен mapping на RdbTypeKind → SQL тип (по PostgreSQL референцията) +- [ ] Auto-increment: SERIAL / BIGSERIAL PRIMARY KEY +- [ ] String/Text: VARCHAR(n), TEXT с default 255 +- [ ] Числови: INTEGER, BIGINT, SMALLINT, DOUBLE PRECISION, DECIMAL(p,s) +- [ ] Дати: DATE, TIME, TIMESTAMP, TIMESTAMPTZ +- [ ] Бинарни: BYTEA +- [ ] Специални: UUID, JSON, JSONB, BOOLEAN, ENUM +- [ ] Foreign key constraints: REFERENCES ... ON DELETE/UPDATE +- [ ] Index генерация +- [ ] Column comments + +Файл: queries/baradb/create_table.nim +- [ ] Да използва create_column_query вместо raw {column.typ} + +Файлове: rename_column.nim, rename_table.nim +- [ ] Fix: `changeTo` → `previousName` (ако е bug) + +Файл: create_migration_table.nim +- [ ] Консистентност: backtick quoting вместо double-quote + +Файл: schema_utils.nim +- [ ] shouldRun() — checksum-based skip (вместо винаги true) +``` + +--- + +### 4. SQL Quoting Inconsistency + +**Проблем:** `baradb_generator.nim` използва backtick `` ` ``, но `create_migration_table.nim` използва double-quote `"`. Трябва да е консистентно. + +**Въпрос:** Какво използва BaraDB сървърът? Ако поддържа и двете — изберем едно. Ако само едно — коригираме навсякъде. + +--- + +### 5. Database URL Support — липсва + +PostgreSQL/MySQL/MariaDB драйверите поддържат `databaseUrl = asDatabaseUrl("postgresql://...")`. BaraDB `dbOpen` приема само позиционни параметри. + +``` +Файл: baradb_open.nim +- [ ] Парсване на `baradb://user:pass@host:port/db` URL +- [ ] Интеграция с `libs/database_url.nim` +``` + +--- + +### 6. Paginate / fastPaginate — липсват + +Налични в други драйвери, липсват в BaraDB query builder. + +``` +Файл: baradb_exec.nim +- [ ] paginate(page, perPage): seq[JsonNode] + metadata +- [ ] fastPaginate(key, perPage): cursor-based +``` + +--- + +### 7. whereNull Bug + +**Проблем:** `baradb_generator.nim:205` — `whereNullSql` референцира `row["symbol"]`, но `baradb_query.nim` `whereNull` не задава `"symbol"` ключ. Ще даде runtime crash. + +``` +Файл: baradb_query.nim — whereNull proc +- [ ] Добавяне на "symbol": "IS" или "IS NOT" в JSON а +``` + +--- + +## Тестове — какво липсва + +| Тестов файл | Съществува за | Липсва за BaraDB | +|-------------|--------------|------------------| +| `test_open.nim` | postgres, sqlite, mysql, mariadb, surreal, **baradb** | — | +| `test_query.nim` | postgres, sqlite, mysql, mariadb, surreal, **baradb** | — | +| `test_prepared_statement.nim` | postgres, sqlite, mysql, mariadb, surreal | **baradb** | +| `test_schema.nim` | postgres, sqlite, mysql, mariadb, surreal | **baradb** | +| `test_create_schema.nim` | postgres, sqlite, mysql, mariadb, surreal | **baradb** | +| `test_pool_wait.nim` | postgres | **baradb** | +| `test_transaction.nim` | postgres, sqlite, mysql, mariadb, surreal | **baradb** | + +--- + +## Приоритети + +| # | Подобрение | Сложност | Ефект | Приоритет | +|---|-----------|----------|-------|-----------| +| 1 | Schema Builder column mapping | Средна | Без него migrations не работят | **ВИСОК** | +| 2 | INSERT RETURNING id | Ниска | Премахва race condition | **ВИСОК** | +| 3 | Prepared Statements | Висока | Security + performance | **СРЕДЕН** | +| 4 | Database URL support | Ниска | UX удобство | **НИСЪК** | +| 5 | Paginate / fastPaginate | Ниска | Feature parity | **НИСЪК** | +| 6 | whereNull fix | Ниска | Bug fix | **ВИСОК** | +| 7 | SQL quoting консистентност | Ниска | Потенциален runtime error | **СРЕДЕН** | +| 8 | Schema utils checksum | Ниска | Migration skip optimization | **НИСЪК** | + +--- + +## Архитектура — файлова карта + +``` +src/allographer/ +├── env.nim # DB_BARADB compile switch ✅ +├── connection.nim # Import/export Baradb типове ✅ +├── query_builder.nim # Import/export baradb модули ✅ +├── schema_builder.nim # Import/export baradb schema ✅ +│ +├── query_builder/ +│ ├── libs/baradb/ +│ │ └── baradb_client.nim # Wire protocol (706 реда) ✅ +│ │ +│ └── models/baradb/ +│ ├── baradb_types.nim # Типове (90 реда) ✅ +│ ├── baradb_open.nim # dbOpen + pool (52 реда) ✅ +│ ├── baradb_query.nim # Fluent API (386 реда) ✅ +│ ├── baradb_exec.nim # Execution (716 реда) ✅⚠️ +│ ├── baradb_transaction.nim # Transaction макро (42 реда) ✅ +│ └── query/ +│ ├── baradb_builder.nim # SQL builder (237 реда) ✅ +│ └── baradb_generator.nim # SQL generator (397 реда) ✅ +│ +├── schema_builder/ +│ ├── queries/baradb/ +│ │ ├── baradb_query_type.nim # Типове (14 реда) ✅ +│ │ ├── create_table.nim # CREATE TABLE (16 реда) ⚠️ skeletal +│ │ ├── add_column.nim # ADD COLUMN (8 реда) ⚠️ minimal +│ │ ├── change_column.nim # ALTER COLUMN (8 реда) ⚠️ minimal +│ │ ├── drop_column.nim # DROP COLUMN (8 реда) ✅ +│ │ ├── drop_table.nim # DROP TABLE (7 реда) ✅ +│ │ ├── rename_column.nim # RENAME COLUMN (8 реда) ⚠️ possible bug +│ │ ├── rename_table.nim # RENAME TABLE (7 реда) ⚠️ possible bug +│ │ ├── reset_table.nim # DELETE FROM (7 реда) ✅ +│ │ ├── create_migration_table.nim (12 реда) ⚠️ quoting inconsistency +│ │ ├── schema_utils.nim # shouldRun (12 реда) ⚠️ always true +│ │ └── sub/ +│ │ ├── create_column_query.nim # (24 реда) ❌ skeletal +│ │ ├── add_column_query.nim # (8 реда) ⚠️ minimal +│ │ └── change_column_query.nim # (6 реда) ⚠️ minimal +│ │ +│ └── usecases/baradb/ +│ ├── create.nim # Table creation flow (24 реда) ✅ +│ ├── alter.nim # Alter flow (45 реда) ✅ +│ ├── drop.nim # Drop flow (18 реда) ✅ +│ ├── create_schema.nim # Code gen from DB (60 реда) ✅ +│ └── create_query_def.nim # Factory (10 реда) ✅ + +tests/baradb/ +├── config.nims # DB_BARADB=true ✅ +├── connections.nim # Connection setup ✅ +├── test_open.nim # Basic connection test ✅ +├── test_query.nim # Integration tests ✅ +├── test_prepared_statement.nim # ❌ липсва +├── test_schema.nim # ❌ липсва +├── test_create_schema.nim # ❌ липсва +├── test_pool_wait.nim # ❌ липсва +└── test_transaction.nim # ❌ липсва +``` + +**Легенда:** ✅ готово | ⚠️ partial/needs work | ❌ липсва | ✅⚠️ работи но с workaround + +--- + +## Краткосрочен план (следващи стъпки) + +### Седмица 1: Schema Builder + Bug fixes +1. `sub/create_column_query.nim` — пълен RdbTypeKind → SQL mapping (по PostgreSQL референция) +2. `create_table.nim` — да използва column query вместо raw тип +3. Fix `whereNull` bug в `baradb_query.nim` +4. Fix quoting inconsistency в `create_migration_table.nim` +5. Fix `rename_column.nim` / `rename_table.nim` ако `changeTo` не съществува + +### Седмица 2: INSERT RETURNING + Prepared Statements (част 1) +1. `insertId` — проучване дали BaraDB поддържа RETURNING, имплементация +2. `prepare()` / `ensureStmt()` — prepared statement кеш +3. `preparedGet()` / `preparedExec()` — изпълнение през `mkQueryParams` + +### Седмица 3: Тестове + Prepared Statements (част 2) +1. `test_schema.nim` — schema builder тестове +2. `test_prepared_statement.nim` — prepared statement тестове +3. `test_transaction.nim` — transaction тестове +4. `test_pool_wait.nim` — pool timeout тестове + +### Седмица 4: Polish +1. Database URL support +2. Paginate / fastPaginate +3. Schema utils checksum +4. Документация в `documents/` diff --git a/clients/nim-allographer/README.md b/clients/nim-allographer/README.md new file mode 100644 index 0000000..e68ba65 --- /dev/null +++ b/clients/nim-allographer/README.md @@ -0,0 +1,204 @@ +allographer +=== + +[![nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble.png)](https://github.com/yglukhov/nimble-tag) +![Build Status](https://github.com/itsumura-h/nim-allographer/workflows/Build%20and%20test%20Nim/badge.svg) + + +An asynchronous query builder library inspired by [Laravel/PHP](https://readouble.com/laravel/6.0/en/queries.html) and [Orator/Python](https://orator-orm.com) for Nim. +Supported Databases are [Sqlite3](https://www.sqlite.org/index.html), [PostgreSQL](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [MariaDB](https://mariadb.org/) and **BaraDB**. (SurrealDB is planned to be supported in v3) +Supported Nim v2 + +## Easy to access Rdb +### Query Builder +```nim +import asyncdispatch, json +import allographer/connection +import allographer/query_builder + +let maxConnections = 95 +let timeout = 30 +let rdb = dbOpen(PostgreSQL, "database", "user", "password", "localhost", 5432, maxConnections, timeout) +# also available +# let rdb = dbOpen(SQLite3, "/path/to/db/sqlite3.db", maxConnections=maxConnections, timeout=timeout) +# let rdb = dbOpen(MySQL, "database", "user", "password", "localhost", 3306, maxConnections, timeout) +# let rdb = dbOpen(MariaDB, "database", "user", "password", "localhost", 3306, maxConnections, timeout) +# let rdb = dbOpen(PostgreSQL, databaseUrl = asDatabaseUrl("postgresql://user:password@localhost:5432/database"), maxConnections=maxConnections, timeout=timeout) +# let rdb = dbOpen(Baradb, "database", "user", "password", "localhost", 9472, maxConnections, timeout) +# let surreal = dbOpen(SurrealDB, "test_ns" "test_db", "user", "password" "http://localhost", 8000, maxConnections, timeout) + +proc main(){.async.} = + let result = await rdb + .table("users") + .select("id", "email", "name") + .limit(5) + .offset(10) + .get() + echo result + +waitFor main() + +>> SELECT id, email, name FROM users LIMIT 5 OFFSET 10 +>> @[ + {"id":11,"email":"user11@gmail.com","name":"user11"}, + {"id":12,"email":"user12@gmail.com","name":"user12"}, + {"id":13,"email":"user13@gmail.com","name":"user13"}, + {"id":14,"email":"user14@gmail.com","name":"user14"}, + {"id":15,"email":"user15@gmail.com","name":"user15"} +] +``` + +### Schema Builder +```nim +import allographer/schema_builder + + +rdb.create([ + table("auth", [ + Column.increments("id"), + Column.string("name").nullable(), + Column.timestamp("created_at").default() + ]), + table("users", [ + Column.increments("id"), + Column.string("name"), + Column.foreign("auth_id").reference("id").on("auth").onDelete(SET_NULL) + ]) +]) + +>> CREATE TABLE auth ( + id INT NOT NULL PRIMARY KEY, + name VARCHAR, + created_at DATETIME DEFAULT (NOW()) +) +>> CREATE TABLE users ( + id INT NOT NULL PRIMARY KEY, + name VARCHAR NOT NULL, + auth_id INT, + FOREIGN KEY(auth_id) REFERENCES auth(id) ON DELETE SET NULL +) + +rdb.alter( + table("users", [ + Column.string("email").unique().default("").add(), + Column.deleteColumn("name") + ]) +) + +>> ALTER TABLE "users" ADD COLUMN 'email' VARCHAR UNIQUE DEFAULT '' CHECK (length("email") <= 255) +>> ALTER TABLE "users" DROP `name` +``` + +## Index + +- [allographer](#allographer) + - [Easy to access Rdb](#easy-to-access-rdb) + - [Query Builder](#query-builder) + - [Schema Builder](#schema-builder) + - [Index](#index) + - [Install](#install) + - [Configuation](#configuation) + - [Createing connection](#createing-connection) + - [Logging](#logging) + - [Documents](#documents) + - [Nim API Documents](#nim-api-documents) + - [v2](#v2) + - [Development](#development) + - [Branch naming rule](#branch-naming-rule) + + + + + +--- + +## Install +```sh +nimble install allographer +``` + +If you get `SIGSEGV: Illegal storage access. (Attempt to read from nil?)` when trying to use the database you likely have a problem with the library path. On OS X the default is to check for the `brew --prefix` of the chosen driver, if that doesn't work it will look in `/usr/lib` or an environment variable `DYLD_xxx_PATH` where `xxx` if your driver: `SQLITE`, `MARIADB`, `MYSQL` or `POSTGRES`. + +## Configuation +Allographer loads emvironment variables of `DB_SQLITE`, `DB_POSTGRES`, `DB_MYSQL`, `DB_MARIADB` and `DB_BARADB` to define which process should be **compiled**.
+(`DB_SURREAL` is planned to be supported in v3)
+These environment variables have to be set at compile time, so they have to be written in `config.nims` not in `.env`. + +config.nims +```nim +import os + +putEnv("DB_SQLITE", $true) +putEnv("DB_POSTGRES", $true) +``` +In this example, even if your runtime environment lacks `mysqlclient-dev`, execution will success. However if your runtime environment lacks `sqlite3`, execution will fail. + +## Createing connection +Database connection should be definded as singleton variable. + +database.nim +```nim +import allographer/connection + +let rdb* = dbOpen(PostgreSQL, "database", "user", "password", "localhost", 5432, maxConnections, timeout) + +# you can create connection for multiple database at same time. +let sqliteRdb* = dbOpen(Sqlite3, "/path/to/db/sqlite3.db", maxConnections=maxConnections, timeout=timeout) +let mysqlRdb* = dbOpen(MySQL, "database", "user", "password", "localhost", 3306, maxConnections, timeout) +let mariaRdb* = dbOpen(MariaDB, "database", "user", "password", "localhost", 3306, maxConnections, timeout) +let pgUrlRdb* = dbOpen(PostgreSQL, databaseUrl = asDatabaseUrl("postgresql://user:password@localhost:5432/database"), maxConnections=maxConnections, timeout=timeout) +let baradbRdb* = dbOpen(Baradb, "database", "user", "password", "localhost", 9472, maxConnections, timeout) +# let surrealDb* = dbOpen(SurrealDb, "test_ns" "test_db", "user", "password" "http://localhost", 8000, maxConnections, timeout) # planned for v3 +``` + +Then, call connection when you run query. + +run_sql.nim +```nim +import asyncdispatch +import allographer/query_builder +from database import rdb + +proc main(){.async.}= + echo await rdb.table("users").get() + +waitFor main() +``` + +## Logging +Please set args in `dbOpen()` +```nim +proc dbOpen*(driver:Driver, database:string="", user:string="", password:string="", + host: string="", port=0, maxConnections=1, timeout=30, + shouldDisplayLog=false, shouldOutputLogFile=false, logDir=""):Rdb +``` +- shouldDisplayLog: Whether display logging in terminal console or not. +- shouldOutputLogFile: Whether output logging in log file or not. +- logDir: Define logging dir path. + + +## Documents +[Schema Builder for RDB](./documents/rdb/schema_builder.md) +[Query Builder for RDB](./documents/rdb/query_builder.md) +[Schema Builder for SurrealDB](./documents/surrealdb/schema_builder.md) (planned for v3) +[Query Builder for SurrealDB](./documents/surrealdb/query_builder.md) (planned for v3) + + +## Nim API Documents +### v2 +[connection](https://itsumura-h.github.io/nim-allographer/v2/connection.html) +[Schema Builder](https://itsumura-h.github.io/nim-allographer/v2/schema_builder.html) +[Query Builder](https://itsumura-h.github.io/nim-allographer/v2/query_builder.html) + + +## Development +### Branch naming rule +Please create this branch name when you will create a new pull request. + +| Branch | Description | +| ------ | ----------- | +| feature-*** | New feature branch | +| hotfix-*** | Bug fix branch | +| chore-*** | Chore work or maintenance | + +This naming rule automatically labels the pull request. diff --git a/clients/nim-allographer/allographer.nimble b/clients/nim-allographer/allographer.nimble new file mode 100644 index 0000000..a817665 --- /dev/null +++ b/clients/nim-allographer/allographer.nimble @@ -0,0 +1,68 @@ +# Package + +version = "0.32.1" +author = "Hidenobu Itsumura @dumblepytech1 as 'medy'" +description = "A Nim query builder library inspired by Laravel/PHP and Orator/Python" +license = "MIT" +srcDir = "src" +# backend = "c" +# bin = @["allographer/cli/dbtool"] +# binDir = "src/bin" +# installExt = @["nim"] +# skipDirs = @["allographer/cli"] + +# Dependencies + +requires "nim >= 2.0.0" +requires "db_connector >= 0.1.0" +requires "checksums >= 0.1.0" + + +import strformat, os + +task test, "run testament test": + exec &"testament p 'tests/sqlite/test_*.nim'" + exec &"testament p 'tests/postgres/test_*.nim'" + exec &"testament p 'tests/mariadb/test_*.nim'" + exec &"testament p 'tests/mysql/test_*.nim'" + exec &"testament p 'tests/surrealdb/test_*.nim'" + exec &"testament p 'tests/utils/test_*.nim'" + + for kind, path in walkDir(getCurrentDir() / "tests"): + if not path.contains(".") and path.fileExists(): + exec "rm -f " & path + +task docs, "Generate API documents": + let + deployDir = &"docs/v{NimMajor}" + pkgDir = srcDir / "allographer" + srcFiles = @[ + "connection", + "query_builder", + "schema_builder", + ] + + if dirExists(deployDir): + rmDir deployDir + for f in srcFiles: + let srcFile = pkgDir / f & ".nim" + exec &"nim doc --hints:off --project --out:{deployDir} --index:on {srcFile}" + + +let toolImage = "basolato:tool" + +task setupTool, "Setup tool docker image": + exec &"docker build -t {toolImage} -f tool_Dockerfile ." + +proc generateToc(dir: string) = + let cwd = getCurrentDir() + for f in listFiles(dir): + if 3 < f.len: + let ext = f[^3..^1] + if ext == ".md": + exec &"docker run --rm -v {cwd}:/work -it {toolImage} --insert --no-backup {f}" + +task toc, "Generate TOC": + generateToc(".") + generateToc("./documents/rdb") + generateToc("./documents/surrealdb") diff --git a/clients/nim-allographer/compose.test.yaml b/clients/nim-allographer/compose.test.yaml new file mode 100644 index 0000000..1e4c429 --- /dev/null +++ b/clients/nim-allographer/compose.test.yaml @@ -0,0 +1,50 @@ +services: + app: + build: + context: . + dockerfile: ./docker/ubuntu/test.Dockerfile + args: + NIM_VERSION: "${NIM_VERSION:-2.2.8}" + tty: true + volumes: + - .:/root/project + depends_on: + - postgres + - mysql + - mariadb + - surreal + + mysql: + image: mysql:8.0-debian + tty: true + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: database + MYSQL_USER: user + MYSQL_PASSWORD: pass + + mariadb: + image: mariadb + tty: true + command: --default-authentication-plugin=mysql_native_password + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: database + MYSQL_USER: user + MYSQL_PASSWORD: pass + + postgres: + image: postgres:alpine + tty: true + environment: + POSTGRES_DB: database + POSTGRES_USER: user + POSTGRES_PASSWORD: pass + + surreal: + image: surrealdb/surrealdb:v3.0 + tty: true + # command: start --log trace --user user --pass pass memory # like redis + # command: start --log trace --user user --pass pass file://root/.surrealdb # like mongo + command: start --log trace --user user --pass pass memory # like in-memory sqlite + # command: start --log trace --user user --pass pass file://root/.surrealdb # like rdb diff --git a/clients/nim-allographer/compose.yaml b/clients/nim-allographer/compose.yaml new file mode 100644 index 0000000..76d29f9 --- /dev/null +++ b/clients/nim-allographer/compose.yaml @@ -0,0 +1,71 @@ +services: + app: + build: + context: . + dockerfile: ./docker/ubuntu/Dockerfile + args: + # VERSION: 1.6.14 + NIM_VERSION: 2.2.8 + tty: true + volumes: + - .:/application + depends_on: + - postgres + - mysql + - mariadb + - surreal + + mysql: + image: mysql:8 + tty: true + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: database + MYSQL_USER: user + MYSQL_PASSWORD: pass + + mariadb: + image: mariadb + tty: true + command: --default-authentication-plugin=mysql_native_password + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: database + MYSQL_USER: user + MYSQL_PASSWORD: pass + + postgres: + image: postgres:alpine + tty: true + environment: + POSTGRES_DB: database + POSTGRES_USER: user + POSTGRES_PASSWORD: pass + + surreal: + image: surrealdb/surrealdb:v3.0 + tty: true + # command: start --log trace --user user --pass pass memory # like redis + # command: start --log trace --user user --pass pass file://root/.surrealdb # like mongo + command: start --log trace --user user --pass pass memory # like in-memory sqlite + # command: start --log trace --user user --pass pass file://root/.surrealdb # like rdb + + adminer: + image: adminer:4.8.1-standalone + tty: true + ports: + - 9001:8080 + depends_on: + - mysql + - mariadb + - postgres + + adminer-sqlite: + build: ./docker/adminer + tty: true + ports: + - 9002:8080 + volumes: + - ./:/app + depends_on: + - app diff --git a/clients/nim-allographer/config.nims b/clients/nim-allographer/config.nims new file mode 100644 index 0000000..ab19512 --- /dev/null +++ b/clients/nim-allographer/config.nims @@ -0,0 +1,7 @@ +import os + +putEnv("DB_SQLITE", $true) +putEnv("DB_POSTGRES", $true) +putEnv("DB_MYSQL", $true) +putEnv("DB_MARIADB", $true) +putEnv("DB_SURREAL", $true) diff --git a/clients/nim-allographer/docker/adminer/Dockerfile b/clients/nim-allographer/docker/adminer/Dockerfile new file mode 100644 index 0000000..9fe3188 --- /dev/null +++ b/clients/nim-allographer/docker/adminer/Dockerfile @@ -0,0 +1,3 @@ +FROM adminer:4.8.1-standalone +USER root +COPY login-password-less.php /var/www/html/plugins-enabled/login-password-less.php diff --git a/clients/nim-allographer/docker/adminer/login-password-less.php b/clients/nim-allographer/docker/adminer/login-password-less.php new file mode 100644 index 0000000..baa9eb7 --- /dev/null +++ b/clients/nim-allographer/docker/adminer/login-password-less.php @@ -0,0 +1,9 @@ +> /etc/apk/repositories +RUN apk update && \ + apk upgrade --no-cache && \ + apk add --no-cache \ + curl \ + gcc \ + musl-dev \ + bsd-compat-headers \ + openssh-client \ + git \ + lcov \ + sqlite \ + libpq \ + mariadb-dev \ + openssl \ + pcre \ + && \ + rm /usr/lib/mysqld* -fr && \ + rm /usr/bin/mysql* -fr +ENV PATH $PATH:/root/.nimble/bin +WORKDIR /root/project +COPY ./allographer.nimble . +RUN nimble install -y +RUN nimble install -y \ + bcrypt \ + progress diff --git a/clients/nim-allographer/docker/ubuntu/Dockerfile b/clients/nim-allographer/docker/ubuntu/Dockerfile new file mode 100644 index 0000000..8a2eeef --- /dev/null +++ b/clients/nim-allographer/docker/ubuntu/Dockerfile @@ -0,0 +1,48 @@ +FROM ubuntu:24.04 + +# prevent timezone dialogue +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt update +RUN apt upgrade -y +RUN apt install -y \ + # for build Nim and make + build-essential \ + # for unzip tar.xz + xz-utils \ + # for https + ca-certificates \ + # for nim regex + libpcre3-dev \ + vim \ + curl \ + git \ + sqlite3 \ + libpq-dev \ + libmariadb-dev + +WORKDIR /root + +# Nim +ARG NIM_VERSION="2.0.0" +RUN curl https://nim-lang.org/choosenim/init.sh -o init.sh +RUN sh init.sh -y +RUN rm -f init.sh +ENV PATH $PATH:/root/.nimble/bin +RUN choosenim ${NIM_VERSION} + +# nimlangserver +# https://github.com/nim-lang/langserver/releases/latest +WORKDIR /root +RUN curl -o nimlangserver.tar.gz -L https://github.com/nim-lang/langserver/releases/latest/download/nimlangserver-linux-amd64.tar.gz +RUN tar zxf nimlangserver.tar.gz +RUN rm -f nimlangserver.tar.gz +RUN mv nimlangserver /root/.nimble/bin/ + +WORKDIR /application +COPY ./allographer.nimble . +RUN nimble install -y -d + +RUN git config --global --add safe.directory /application + +WORKDIR /application diff --git a/clients/nim-allographer/docker/ubuntu/test.Dockerfile b/clients/nim-allographer/docker/ubuntu/test.Dockerfile new file mode 100644 index 0000000..8f8c82c --- /dev/null +++ b/clients/nim-allographer/docker/ubuntu/test.Dockerfile @@ -0,0 +1,40 @@ +FROM ubuntu:24.04 + +# prevent timezone dialogue +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt update +RUN apt upgrade -y +RUN apt install -y \ + gcc \ + xz-utils \ + ca-certificates \ + libpcre3-dev \ + curl \ + git \ + sqlite3 \ + libpq-dev \ + libmariadb-dev + +# gcc... for Nim +# xz-utils... for unzip tar.xz +# ca-certificates... for https +# libpcre3-dev... for nim regex + +WORKDIR /root + +# Nim +ARG NIM_VERSION="2.0.0" +RUN curl https://nim-lang.org/choosenim/init.sh -o init.sh +RUN sh init.sh -y +RUN rm -f init.sh +ENV PATH $PATH:/root/.nimble/bin +RUN choosenim ${NIM_VERSION} + +WORKDIR /root/project +COPY ./allographer.nimble . +RUN nimble install -y -d + +RUN git config --global --add safe.directory /root/project + +WORKDIR /root/project diff --git a/clients/nim-allographer/docs/connection.html b/clients/nim-allographer/docs/connection.html new file mode 100644 index 0000000..a56062f --- /dev/null +++ b/clients/nim-allographer/docs/connection.html @@ -0,0 +1,93 @@ + + + + + + + +src/allographer/connection + + + + + + + + + + + + + + + + + + + diff --git a/clients/nim-allographer/docs/connection.idx b/clients/nim-allographer/docs/connection.idx new file mode 100644 index 0000000..34ca3d8 --- /dev/null +++ b/clients/nim-allographer/docs/connection.idx @@ -0,0 +1 @@ +nimTitle connection connection.html module src/allographer/connection 0 diff --git a/clients/nim-allographer/docs/dochack.js b/clients/nim-allographer/docs/dochack.js new file mode 100644 index 0000000..854c529 --- /dev/null +++ b/clients/nim-allographer/docs/dochack.js @@ -0,0 +1,1607 @@ +/* Generated by the Nim Compiler v2.0.0 */ +var framePtr = null; +var excHandler = 0; +var lastJSError = null; +var NTI33554466 = {size: 0,kind: 1,base: null,node: null,finalizer: null}; +var NTI721420302 = {size: 0, kind: 18, base: null, node: null, finalizer: null}; +var NTI33554435 = {size: 0,kind: 31,base: null,node: null,finalizer: null}; +var NTI973078607 = {size: 0,kind: 31,base: null,node: null,finalizer: null}; +var NTI973078613 = {size: 0, kind: 18, base: null, node: null, finalizer: null}; +var NTI134217745 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217749 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217751 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI33555173 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI33555181 = {size: 0, kind: 22, base: null, node: null, finalizer: null}; +var NTI33554449 = {size: 0,kind: 28,base: null,node: null,finalizer: null}; +var NTI33554450 = {size: 0,kind: 29,base: null,node: null,finalizer: null}; +var NTI33555180 = {size: 0, kind: 22, base: null, node: null, finalizer: null}; +var NTI33555177 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI33555178 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217741 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217743 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NNI134217743 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217743.node = NNI134217743; +var NNI134217741 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217741.node = NNI134217741; +var NNI33555178 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI33555178.node = NNI33555178; +NTI33555180.base = NTI33555177; +NTI33555181.base = NTI33555177; +var NNI33555177 = {kind: 2, len: 5, offset: 0, typ: null, name: null, sons: [{kind: 1, offset: "parent", len: 0, typ: NTI33555180, name: "parent", sons: null}, +{kind: 1, offset: "name", len: 0, typ: NTI33554450, name: "name", sons: null}, +{kind: 1, offset: "message", len: 0, typ: NTI33554449, name: "msg", sons: null}, +{kind: 1, offset: "trace", len: 0, typ: NTI33554449, name: "trace", sons: null}, +{kind: 1, offset: "up", len: 0, typ: NTI33555181, name: "up", sons: null}]}; +NTI33555177.node = NNI33555177; +var NNI33555173 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI33555173.node = NNI33555173; +NTI33555177.base = NTI33555173; +NTI33555178.base = NTI33555177; +NTI134217741.base = NTI33555178; +NTI134217743.base = NTI134217741; +var NNI134217751 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217751.node = NNI134217751; +NTI134217751.base = NTI33555178; +var NNI134217749 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217749.node = NNI134217749; +NTI134217749.base = NTI33555178; +var NNI134217745 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217745.node = NNI134217745; +NTI134217745.base = NTI33555178; +var NNI973078613 = {kind: 2, len: 2, offset: 0, typ: null, name: null, sons: [{kind: 1, offset: "a", len: 0, typ: NTI973078607, name: "a", sons: null}, +{kind: 1, offset: "b", len: 0, typ: NTI33554435, name: "b", sons: null}]}; +NTI973078613.node = NNI973078613; +var NNI721420302 = {kind: 2, len: 2, offset: 0, typ: null, name: null, sons: [{kind: 1, offset: "Field0", len: 0, typ: NTI33554435, name: "Field0", sons: null}, +{kind: 1, offset: "Field1", len: 0, typ: NTI33554466, name: "Field1", sons: null}]}; +NTI721420302.node = NNI721420302; + +function mnewString(len_33557003) { + return new Array(len_33557003); + + + +} + +function toJSStr(s_33556901) { + var result_33556902 = null; + + var res_33556943 = newSeq_33556919((s_33556901).length); + var i_33556944 = 0; + var j_33556945 = 0; + Label1: { + Label2: while (true) { + if (!(i_33556944 < (s_33556901).length)) break Label2; + var c_33556946 = s_33556901[i_33556944]; + if ((c_33556946 < 128)) { + res_33556943[j_33556945] = String.fromCharCode(c_33556946); + i_33556944 += 1; + } + else { + var helper_33556959 = newSeq_33556919(0); + Label3: { + Label4: while (true) { + if (!true) break Label4; + var code_33556960 = c_33556946.toString(16); + if ((((code_33556960) == null ? 0 : (code_33556960).length) == 1)) { + helper_33556959.push("%0");; + } + else { + helper_33556959.push("%");; + } + + helper_33556959.push(code_33556960);; + i_33556944 += 1; + if ((((s_33556901).length <= i_33556944) || (s_33556901[i_33556944] < 128))) { + break Label3; + } + + c_33556946 = s_33556901[i_33556944]; + } + }; +++excHandler; + try { + res_33556943[j_33556945] = decodeURIComponent(helper_33556959.join("")); +--excHandler; +} catch (EXCEPTION) { + var prevJSError = lastJSError; + lastJSError = EXCEPTION; + --excHandler; + res_33556943[j_33556945] = helper_33556959.join(""); + lastJSError = prevJSError; + } finally { + } + } + + j_33556945 += 1; + } + }; + if (res_33556943.length < j_33556945) { for (var i = res_33556943.length ; i < j_33556945 ; ++i) res_33556943.push(null); } + else { res_33556943.length = j_33556945; }; + result_33556902 = res_33556943.join(""); + + return result_33556902; + +} + +function raiseException(e_33556653, ename_33556654) { + e_33556653.name = ename_33556654; + if ((excHandler == 0)) { + unhandledException(e_33556653); + } + + throw e_33556653; + + +} + +function addInt(a_33557050, b_33557051) { + var result = a_33557050 + b_33557051; + checkOverflowInt(result); + return result; + + + +} + +function chckRange(i_33557324, a_33557325, b_33557326) { + var result_33557327 = 0; + + BeforeRet: { + if (((a_33557325 <= i_33557324) && (i_33557324 <= b_33557326))) { + result_33557327 = i_33557324; + break BeforeRet; + } + else { + raiseRangeError(); + } + + }; + + return result_33557327; + +} + +function setConstr() { + var result = {}; + for (var i = 0; i < arguments.length; ++i) { + var x = arguments[i]; + if (typeof(x) == "object") { + for (var j = x[0]; j <= x[1]; ++j) { + result[j] = true; + } + } else { + result[x] = true; + } + } + return result; + + + +} +var ConstSet1 = setConstr(17, 16, 4, 18, 27, 19, 23, 22, 21); + +function nimCopy(dest_33557268, src_33557269, ti_33557270) { + var result_33557279 = null; + + switch (ti_33557270.kind) { + case 21: + case 22: + case 23: + case 5: + if (!(isFatPointer_33557259(ti_33557270))) { + result_33557279 = src_33557269; + } + else { + result_33557279 = [src_33557269[0], src_33557269[1]]; + } + + break; + case 19: + if (dest_33557268 === null || dest_33557268 === undefined) { + dest_33557268 = {}; + } + else { + for (var key in dest_33557268) { delete dest_33557268[key]; } + } + for (var key in src_33557269) { dest_33557268[key] = src_33557269[key]; } + result_33557279 = dest_33557268; + + break; + case 18: + case 17: + if (!((ti_33557270.base == null))) { + result_33557279 = nimCopy(dest_33557268, src_33557269, ti_33557270.base); + } + else { + if ((ti_33557270.kind == 17)) { + result_33557279 = (dest_33557268 === null || dest_33557268 === undefined) ? {m_type: ti_33557270} : dest_33557268; + } + else { + result_33557279 = (dest_33557268 === null || dest_33557268 === undefined) ? {} : dest_33557268; + } + } + nimCopyAux(result_33557279, src_33557269, ti_33557270.node); + break; + case 4: + case 16: + if(ArrayBuffer.isView(src_33557269)) { + if(dest_33557268 === null || dest_33557268 === undefined || dest_33557268.length != src_33557269.length) { + dest_33557268 = new src_33557269.constructor(src_33557269); + } else { + dest_33557268.set(src_33557269, 0); + } + result_33557279 = dest_33557268; + } else { + if (src_33557269 === null) { + result_33557279 = null; + } + else { + if (dest_33557268 === null || dest_33557268 === undefined || dest_33557268.length != src_33557269.length) { + dest_33557268 = new Array(src_33557269.length); + } + result_33557279 = dest_33557268; + for (var i = 0; i < src_33557269.length; ++i) { + result_33557279[i] = nimCopy(result_33557279[i], src_33557269[i], ti_33557270.base); + } + } + } + + break; + case 24: + case 27: + if (src_33557269 === null) { + result_33557279 = null; + } + else { + if (dest_33557268 === null || dest_33557268 === undefined || dest_33557268.length != src_33557269.length) { + dest_33557268 = new Array(src_33557269.length); + } + result_33557279 = dest_33557268; + for (var i = 0; i < src_33557269.length; ++i) { + result_33557279[i] = nimCopy(result_33557279[i], src_33557269[i], ti_33557270.base); + } + } + + break; + case 28: + if (src_33557269 !== null) { + result_33557279 = src_33557269.slice(0); + } + + break; + default: + result_33557279 = src_33557269; + break; + } + + return result_33557279; + +} + +function chckIndx(i_33557319, a_33557320, b_33557321) { + var result_33557322 = 0; + + BeforeRet: { + if (((a_33557320 <= i_33557319) && (i_33557319 <= b_33557321))) { + result_33557322 = i_33557319; + break BeforeRet; + } + else { + raiseIndexError(i_33557319, a_33557320, b_33557321); + } + + }; + + return result_33557322; + +} + +function makeNimstrLit(c_33556895) { + var result = []; + for (var i = 0; i < c_33556895.length; ++i) { + result[i] = c_33556895.charCodeAt(i); + } + return result; + + + +} + +function subInt(a_33557054, b_33557055) { + var result = a_33557054 - b_33557055; + checkOverflowInt(result); + return result; + + + +} + +function cstrToNimstr(c_33556898) { + var ln = c_33556898.length; + var result = new Array(ln); + var r = 0; + for (var i = 0; i < ln; ++i) { + var ch = c_33556898.charCodeAt(i); + + if (ch < 128) { + result[r] = ch; + } + else { + if (ch < 2048) { + result[r] = (ch >> 6) | 192; + } + else { + if (ch < 55296 || ch >= 57344) { + result[r] = (ch >> 12) | 224; + } + else { + ++i; + ch = 65536 + (((ch & 1023) << 10) | (c_33556898.charCodeAt(i) & 1023)); + result[r] = (ch >> 18) | 240; + ++r; + result[r] = ((ch >> 12) & 63) | 128; + } + ++r; + result[r] = ((ch >> 6) & 63) | 128; + } + ++r; + result[r] = (ch & 63) | 128; + } + ++r; + } + return result; + + + +} +var ConstSet2 = setConstr([65, 90]); +var ConstSet3 = setConstr(95, 32, 46); +var ConstSet4 = setConstr(95, 32, 46); + +function mulInt(a_33557058, b_33557059) { + var result = a_33557058 * b_33557059; + checkOverflowInt(result); + return result; + + + +} +var ConstSet5 = setConstr([97, 122]); +var ConstSet6 = setConstr([65, 90], [97, 122]); +var ConstSet7 = setConstr([97, 122]); +var ConstSet8 = setConstr([65, 90]); +var ConstSet9 = setConstr([65, 90], [97, 122]); + +function nimMax(a_33557108, b_33557109) { + var Temporary1; + + var result_33557110 = 0; + + BeforeRet: { + if ((b_33557109 <= a_33557108)) { + Temporary1 = a_33557108; + } + else { + Temporary1 = b_33557109; + } + + result_33557110 = Temporary1; + break BeforeRet; + }; + + return result_33557110; + +} + +function nimMin(a_33557104, b_33557105) { + var Temporary1; + + var result_33557106 = 0; + + BeforeRet: { + if ((a_33557104 <= b_33557105)) { + Temporary1 = a_33557104; + } + else { + Temporary1 = b_33557105; + } + + result_33557106 = Temporary1; + break BeforeRet; + }; + + return result_33557106; + +} + +function addChar(x_33557415, c_33557416) { + x_33557415.push(c_33557416); + + +} +var objectID_1207959729 = [0]; + +function setTheme(theme_570425350) { + document.documentElement.setAttribute("data-theme", theme_570425350); + window.localStorage.setItem("theme", theme_570425350); + + +} + +function add_33556373(x_33556374, x_33556374_Idx, y_33556375) { + if (x_33556374[x_33556374_Idx] === null) { x_33556374[x_33556374_Idx] = []; } + var off = x_33556374[x_33556374_Idx].length; + x_33556374[x_33556374_Idx].length += y_33556375.length; + for (var i = 0; i < y_33556375.length; ++i) { + x_33556374[x_33556374_Idx][off+i] = y_33556375.charCodeAt(i); + } + + + +} + +function newSeq_33556919(len_33556921) { + var result_33556922 = []; + + result_33556922 = new Array(len_33556921); for (var i = 0 ; i < len_33556921 ; ++i) { result_33556922[i] = null; } + return result_33556922; + +} + +function unhandledException(e_33556649) { + var buf_33556650 = [[]]; + if (!(((e_33556649.message).length == 0))) { + buf_33556650[0].push.apply(buf_33556650[0], [69,114,114,111,114,58,32,117,110,104,97,110,100,108,101,100,32,101,120,99,101,112,116,105,111,110,58,32]);; + buf_33556650[0].push.apply(buf_33556650[0], e_33556649.message);; + } + else { + buf_33556650[0].push.apply(buf_33556650[0], [69,114,114,111,114,58,32,117,110,104,97,110,100,108,101,100,32,101,120,99,101,112,116,105,111,110]);; + } + + buf_33556650[0].push.apply(buf_33556650[0], [32,91]);; + add_33556373(buf_33556650, 0, e_33556649.name); + buf_33556650[0].push.apply(buf_33556650[0], [93,10]);; + var cbuf_33556651 = toJSStr(buf_33556650[0]); + if (typeof(Error) !== "undefined") { + throw new Error(cbuf_33556651); + } + else { + throw cbuf_33556651; + } + + + +} + +function raiseOverflow() { + raiseException({message: [111,118,101,114,45,32,111,114,32,117,110,100,101,114,102,108,111,119], parent: null, m_type: NTI134217743, name: null, trace: [], up: null}, "OverflowDefect"); + + +} + +function checkOverflowInt(a_33557048) { + if (a_33557048 > 2147483647 || a_33557048 < -2147483648) raiseOverflow(); + + + +} + +function raiseRangeError() { + raiseException({message: [118,97,108,117,101,32,111,117,116,32,111,102,32,114,97,110,103,101], parent: null, m_type: NTI134217751, name: null, trace: [], up: null}, "RangeDefect"); + + +} + +function addChars_301990090(result_301990092, result_301990092_Idx, x_301990093, start_301990094, n_301990095) { + var Temporary1; + + var old_301990096 = (result_301990092[result_301990092_Idx]).length; + if (result_301990092[result_301990092_Idx].length < (Temporary1 = chckRange(addInt(old_301990096, n_301990095), 0, 2147483647), Temporary1)) { for (var i = result_301990092[result_301990092_Idx].length; i < Temporary1; ++i) result_301990092[result_301990092_Idx].push(0); } + else {result_301990092[result_301990092_Idx].length = Temporary1; }; + Label2: { + var iHEX60gensym4_301990110 = 0; + var i_570426543 = 0; + Label3: { + Label4: while (true) { + if (!(i_570426543 < n_301990095)) break Label4; + iHEX60gensym4_301990110 = i_570426543; + result_301990092[result_301990092_Idx][chckIndx(addInt(old_301990096, iHEX60gensym4_301990110), 0, (result_301990092[result_301990092_Idx]).length - 1)] = x_301990093.charCodeAt(chckIndx(addInt(start_301990094, iHEX60gensym4_301990110), 0, (x_301990093).length - 1)); + i_570426543 = addInt(i_570426543, 1); + } + }; + }; + + +} + +function addChars_301990086(result_301990088, result_301990088_Idx, x_301990089) { + addChars_301990090(result_301990088, result_301990088_Idx, x_301990089, 0, ((x_301990089) == null ? 0 : (x_301990089).length)); + + +} + +function addInt_301990111(result_301990112, result_301990112_Idx, x_301990113) { + addChars_301990086(result_301990112, result_301990112_Idx, ((x_301990113) + "")); + + +} + +function addInt_301990129(result_301990130, result_301990130_Idx, x_301990131) { + addInt_301990111(result_301990130, result_301990130_Idx, BigInt(x_301990131)); + + +} + +function HEX24_385875976(x_385875977) { + var result_385875978 = [[]]; + + addInt_301990129(result_385875978, 0, x_385875977); + + return result_385875978[0]; + +} + +function isFatPointer_33557259(ti_33557260) { + var result_33557261 = false; + + BeforeRet: { + result_33557261 = !((ConstSet1[ti_33557260.base.kind] != undefined)); + break BeforeRet; + }; + + return result_33557261; + +} + +function nimCopyAux(dest_33557272, src_33557273, n_33557274) { + switch (n_33557274.kind) { + case 0: + break; + case 1: + dest_33557272[n_33557274.offset] = nimCopy(dest_33557272[n_33557274.offset], src_33557273[n_33557274.offset], n_33557274.typ); + + break; + case 2: + for (var i = 0; i < n_33557274.sons.length; i++) { + nimCopyAux(dest_33557272, src_33557273, n_33557274.sons[i]); + } + + break; + case 3: + dest_33557272[n_33557274.offset] = nimCopy(dest_33557272[n_33557274.offset], src_33557273[n_33557274.offset], n_33557274.typ); + for (var i = 0; i < n_33557274.sons.length; ++i) { + nimCopyAux(dest_33557272, src_33557273, n_33557274.sons[i][1]); + } + + break; + } + + +} + +function raiseIndexError(i_33556812, a_33556813, b_33556814) { + var Temporary1; + + if ((b_33556814 < a_33556813)) { + Temporary1 = [105,110,100,101,120,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,116,104,101,32,99,111,110,116,97,105,110,101,114,32,105,115,32,101,109,112,116,121]; + } + else { + Temporary1 = ([105,110,100,101,120,32] || []).concat(HEX24_385875976(i_33556812) || [],[32,110,111,116,32,105,110,32] || [],HEX24_385875976(a_33556813) || [],[32,46,46,32] || [],HEX24_385875976(b_33556814) || []); + } + + raiseException({message: nimCopy(null, Temporary1, NTI33554449), parent: null, m_type: NTI134217749, name: null, trace: [], up: null}, "IndexDefect"); + + +} + +function sysFatal_268435501(message_268435504) { + raiseException({message: nimCopy(null, message_268435504, NTI33554449), m_type: NTI134217745, parent: null, name: null, trace: [], up: null}, "AssertionDefect"); + + +} + +function raiseAssert_268435499(msg_268435500) { + sysFatal_268435501(msg_268435500); + + +} + +function failedAssertImpl_268435541(msg_268435542) { + raiseAssert_268435499(msg_268435542); + + +} + +function onDOMLoaded(e_570425385) { + +function HEX3Aanonymous_570425409(event_570425410) { + event_570425410.target.parentNode.style.display = "none"; + event_570425410.target.parentNode.nextSibling.style.display = "inline"; + + + } + + document.getElementById("theme-select").value = window.localStorage.getItem("theme"); + Label1: { + var pragmaDots_570425408 = null; + var colontmp__570426534 = []; + colontmp__570426534 = document.getElementsByClassName("pragmadots"); + var i_570426536 = 0; + var L_570426537 = (colontmp__570426534).length; + Label2: { + Label3: while (true) { + if (!(i_570426536 < L_570426537)) break Label3; + pragmaDots_570425408 = colontmp__570426534[chckIndx(i_570426536, 0, (colontmp__570426534).length - 1)]; + pragmaDots_570425408.onclick = HEX3Aanonymous_570425409; + i_570426536 = addInt(i_570426536, 1); + if (!(((colontmp__570426534).length == L_570426537))) { + failedAssertImpl_268435541(makeNimstrLit("iterators.nim(246, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + + +} + +function isWhitespace_570425752(x_570425753) { + var result_570425754 = false; + + result_570425754 = (((x_570425753.nodeName == "#text") && !/\S/.test(x_570425753.textContent)) || (x_570425753.nodeName == "#comment")); + + return result_570425754; + +} + +function toToc_570425755(x_570425756, father_570425757) { + var Temporary5; + var Temporary6; + var Temporary7; + var Temporary8; + var Temporary15; + + if ((x_570425756.nodeName == "UL")) { + var f_570425765 = {heading: null, kids: [], sortId: (father_570425757.kids).length, doSort: false}; + var i_570425766 = 0; + Label1: { + Label2: while (true) { + if (!(i_570425766 < x_570425756.childNodes.length)) break Label2; + var nxt_570425767 = addInt(i_570425766, 1); + Label3: { + Label4: while (true) { + if (!(nxt_570425767 < x_570425756.childNodes.length)) Temporary5 = false; else { Temporary5 = isWhitespace_570425752(x_570425756.childNodes[nxt_570425767]); } if (!Temporary5) break Label4; + nxt_570425767 = addInt(nxt_570425767, 1); + } + }; + if (!(nxt_570425767 < x_570425756.childNodes.length)) Temporary8 = false; else { Temporary8 = (x_570425756.childNodes[i_570425766].nodeName == "LI"); } if (!Temporary8) Temporary7 = false; else { Temporary7 = (x_570425756.childNodes[i_570425766].childNodes.length == 1); } if (!Temporary7) Temporary6 = false; else { Temporary6 = (x_570425756.childNodes[nxt_570425767].nodeName == "UL"); } if (Temporary6) { + var e_570425780 = {heading: x_570425756.childNodes[i_570425766].childNodes[0], kids: [], sortId: (f_570425765.kids).length, doSort: false}; + var it_570425781 = x_570425756.childNodes[nxt_570425767]; + Label9: { + var j_570425786 = 0; + var colontmp__570426550 = 0; + colontmp__570426550 = it_570425781.childNodes.length; + var i_570426551 = 0; + Label10: { + Label11: while (true) { + if (!(i_570426551 < colontmp__570426550)) break Label11; + j_570425786 = i_570426551; + toToc_570425755(it_570425781.childNodes[j_570425786], e_570425780); + i_570426551 = addInt(i_570426551, 1); + } + }; + }; + f_570425765.kids.push(e_570425780);; + i_570425766 = addInt(nxt_570425767, 1); + } + else { + toToc_570425755(x_570425756.childNodes[i_570425766], f_570425765); + i_570425766 = addInt(i_570425766, 1); + } + + } + }; + father_570425757.kids.push(f_570425765);; + } + else { + if (isWhitespace_570425752(x_570425756)) { + } + else { + if ((x_570425756.nodeName == "LI")) { + var idx_570425804 = []; + Label12: { + var i_570425809 = 0; + var colontmp__570426554 = 0; + colontmp__570426554 = x_570425756.childNodes.length; + var i_570426555 = 0; + Label13: { + Label14: while (true) { + if (!(i_570426555 < colontmp__570426554)) break Label14; + i_570425809 = i_570426555; + if (!(isWhitespace_570425752(x_570425756.childNodes[i_570425809]))) { + idx_570425804.push(i_570425809);; + } + + i_570426555 = addInt(i_570426555, 1); + } + }; + }; + if (!((idx_570425804).length == 2)) Temporary15 = false; else { Temporary15 = (x_570425756.childNodes[idx_570425804[chckIndx(1, 0, (idx_570425804).length - 1)]].nodeName == "UL"); } if (Temporary15) { + var e_570425825 = {heading: x_570425756.childNodes[idx_570425804[chckIndx(0, 0, (idx_570425804).length - 1)]], kids: [], sortId: (father_570425757.kids).length, doSort: false}; + var it_570425826 = x_570425756.childNodes[idx_570425804[chckIndx(1, 0, (idx_570425804).length - 1)]]; + Label16: { + var j_570425831 = 0; + var colontmp__570426558 = 0; + colontmp__570426558 = it_570425826.childNodes.length; + var i_570426559 = 0; + Label17: { + Label18: while (true) { + if (!(i_570426559 < colontmp__570426558)) break Label18; + j_570425831 = i_570426559; + toToc_570425755(it_570425826.childNodes[j_570425831], e_570425825); + i_570426559 = addInt(i_570426559, 1); + } + }; + }; + father_570425757.kids.push(e_570425825);; + } + else { + Label19: { + var i_570425840 = 0; + var colontmp__570426562 = 0; + colontmp__570426562 = x_570425756.childNodes.length; + var i_570426563 = 0; + Label20: { + Label21: while (true) { + if (!(i_570426563 < colontmp__570426562)) break Label21; + i_570425840 = i_570426563; + toToc_570425755(x_570425756.childNodes[i_570425840], father_570425757); + i_570426563 = addInt(i_570426563, 1); + } + }; + }; + } + + } + else { + father_570425757.kids.push({heading: x_570425756, kids: [], sortId: (father_570425757.kids).length, doSort: false});; + } + }} + + +} + +function extractItems_570425543(x_570425544, heading_570425545, items_570425546, items_570425546_Idx) { + BeforeRet: { + if ((x_570425544 == null)) { + break BeforeRet; + } + + if ((!((x_570425544.heading == null)) && (x_570425544.heading.textContent == heading_570425545))) { + Label1: { + var i_570425563 = 0; + var colontmp__570426566 = 0; + colontmp__570426566 = (x_570425544.kids).length; + var i_570426567 = 0; + Label2: { + Label3: while (true) { + if (!(i_570426567 < colontmp__570426566)) break Label3; + i_570425563 = i_570426567; + items_570425546[items_570425546_Idx].push(x_570425544.kids[chckIndx(i_570425563, 0, (x_570425544.kids).length - 1)].heading);; + i_570426567 = addInt(i_570426567, 1); + } + }; + }; + } + else { + Label4: { + var k_570425589 = null; + var i_570426571 = 0; + var L_570426572 = (x_570425544.kids).length; + Label5: { + Label6: while (true) { + if (!(i_570426571 < L_570426572)) break Label6; + k_570425589 = x_570425544.kids[chckIndx(i_570426571, 0, (x_570425544.kids).length - 1)]; + extractItems_570425543(k_570425589, heading_570425545, items_570425546, items_570425546_Idx); + i_570426571 = addInt(i_570426571, 1); + if (!(((x_570425544.kids).length == L_570426572))) { + failedAssertImpl_268435541(makeNimstrLit("iterators.nim(246, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + } + + }; + + +} + +function tree_570425474(tag_570425475, kids_570425476) { + var result_570425477 = null; + + result_570425477 = document.createElement(tag_570425475); + Label1: { + var k_570425491 = null; + var i_570426584 = 0; + Label2: { + Label3: while (true) { + if (!(i_570426584 < (kids_570425476).length)) break Label3; + k_570425491 = kids_570425476[chckIndx(i_570426584, 0, (kids_570425476).length - 1)]; + result_570425477.appendChild(k_570425491); + i_570426584 = addInt(i_570426584, 1); + } + }; + }; + + return result_570425477; + +} + +function text_570425499(s_570425500) { + var result_570425501 = null; + + result_570425501 = document.createTextNode(s_570425500); + + return result_570425501; + +} + +function uncovered_570425944(x_570425945) { + var Temporary1; + + var result_570425946 = null; + + BeforeRet: { + if ((((x_570425945.kids).length == 0) && !((x_570425945.heading == null)))) { + if (!(x_570425945.heading.hasOwnProperty('__karaxMarker__'))) { + Temporary1 = x_570425945; + } + else { + Temporary1 = null; + } + + result_570425946 = Temporary1; + break BeforeRet; + } + + result_570425946 = {heading: x_570425945.heading, kids: [], sortId: x_570425945.sortId, doSort: x_570425945.doSort}; + Label2: { + var k_570425961 = null; + var i_570426591 = 0; + var L_570426592 = (x_570425945.kids).length; + Label3: { + Label4: while (true) { + if (!(i_570426591 < L_570426592)) break Label4; + k_570425961 = x_570425945.kids[chckIndx(i_570426591, 0, (x_570425945.kids).length - 1)]; + var y_570425962 = uncovered_570425944(k_570425961); + if (!((y_570425962 == null))) { + result_570425946.kids.push(y_570425962);; + } + + i_570426591 = addInt(i_570426591, 1); + if (!(((x_570425945.kids).length == L_570426592))) { + failedAssertImpl_268435541(makeNimstrLit("iterators.nim(246, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + if (((result_570425946.kids).length == 0)) { + result_570425946 = null; + } + + }; + + return result_570425946; + +} + +function mergeTocs_570425974(orig_570425975, news_570425976) { + var result_570425977 = null; + + result_570425977 = uncovered_570425944(orig_570425975); + if ((result_570425977 == null)) { + result_570425977 = news_570425976; + } + else { + Label1: { + var i_570425989 = 0; + var colontmp__570426587 = 0; + colontmp__570426587 = (news_570425976.kids).length; + var i_570426588 = 0; + Label2: { + Label3: while (true) { + if (!(i_570426588 < colontmp__570426587)) break Label3; + i_570425989 = i_570426588; + result_570425977.kids.push(news_570425976.kids[chckIndx(i_570425989, 0, (news_570425976.kids).length - 1)]);; + i_570426588 = addInt(i_570426588, 1); + } + }; + }; + } + + + return result_570425977; + +} + +function buildToc_570425994(orig_570425995, types_570425996, procs_570425997) { + var result_570425998 = null; + + var newStuff_570426003 = {heading: null, kids: [], doSort: true, sortId: 0}; + Label1: { + var t_570426007 = null; + var i_570426579 = 0; + var L_570426580 = (types_570425996).length; + Label2: { + Label3: while (true) { + if (!(i_570426579 < L_570426580)) break Label3; + t_570426007 = types_570425996[chckIndx(i_570426579, 0, (types_570425996).length - 1)]; + var c_570426012 = {heading: t_570426007.cloneNode(true), kids: [], doSort: true, sortId: 0}; + t_570426007.__karaxMarker__ = true; + Label4: { + var p_570426016 = null; + var i_570426576 = 0; + var L_570426577 = (procs_570425997).length; + Label5: { + Label6: while (true) { + if (!(i_570426576 < L_570426577)) break Label6; + p_570426016 = procs_570425997[chckIndx(i_570426576, 0, (procs_570425997).length - 1)]; + if (!(p_570426016.hasOwnProperty('__karaxMarker__'))) { + var xx_570426017 = p_570426016.parentNode.getElementsByClassName("attachedType"); + if ((((xx_570426017).length == 1) && (xx_570426017[chckIndx(0, 0, (xx_570426017).length - 1)].textContent == t_570426007.textContent))) { + var q_570426022 = tree_570425474("A", [text_570425499(p_570426016.title)]); + q_570426022.setAttribute("href", p_570426016.getAttribute("href")); + c_570426012.kids.push({heading: q_570426022, kids: [], sortId: 0, doSort: false});; + p_570426016.__karaxMarker__ = true; + } + + } + + i_570426576 = addInt(i_570426576, 1); + if (!(((procs_570425997).length == L_570426577))) { + failedAssertImpl_268435541(makeNimstrLit("iterators.nim(246, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + newStuff_570426003.kids.push(c_570426012);; + i_570426579 = addInt(i_570426579, 1); + if (!(((types_570425996).length == L_570426580))) { + failedAssertImpl_268435541(makeNimstrLit("iterators.nim(246, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + result_570425998 = mergeTocs_570425974(orig_570425995, newStuff_570426003); + + return result_570425998; + +} + +function add_570425492(parent_570425493, kid_570425494) { + if (((parent_570425493.nodeName == "TR") && ((kid_570425494.nodeName == "TD") || (kid_570425494.nodeName == "TH")))) { + var k_570425495 = document.createElement("TD"); + k_570425495.appendChild(kid_570425494); + parent_570425493.appendChild(k_570425495); + } + else { + parent_570425493.appendChild(kid_570425494); + } + + + +} + +function setClass_570425496(e_570425497, value_570425498) { + e_570425497.setAttribute("class", value_570425498); + + +} + +function toHtml_570425622(x_570425623, isRoot_570425624) { + +function HEX3Aanonymous_570425642(a_570425643, b_570425644) { + var result_570425645 = 0; + + BeforeRet: { + if ((!((a_570425643.heading == null)) && !((b_570425644.heading == null)))) { + var x_570425654 = a_570425643.heading.textContent; + var y_570425655 = b_570425644.heading.textContent; + if ((x_570425654 < y_570425655)) { + result_570425645 = (-1); + break BeforeRet; + } + + if ((y_570425655 < x_570425654)) { + result_570425645 = 1; + break BeforeRet; + } + + result_570425645 = 0; + break BeforeRet; + } + else { + result_570425645 = subInt(a_570425643.sortId, b_570425644.sortId); + break BeforeRet; + } + + }; + + return result_570425645; + + } + + var result_570425625 = null; + + BeforeRet: { + if ((x_570425623 == null)) { + result_570425625 = null; + break BeforeRet; + } + + if (((x_570425623.kids).length == 0)) { + if ((x_570425623.heading == null)) { + result_570425625 = null; + break BeforeRet; + } + + result_570425625 = x_570425623.heading.cloneNode(true); + break BeforeRet; + } + + result_570425625 = tree_570425474("DIV", []); + if ((!((x_570425623.heading == null)) && !(x_570425623.heading.hasOwnProperty('__karaxMarker__')))) { + add_570425492(result_570425625, x_570425623.heading.cloneNode(true)); + } + + var ul_570425641 = tree_570425474("UL", []); + if (isRoot_570425624) { + setClass_570425496(ul_570425641, "simple simple-toc"); + } + else { + setClass_570425496(ul_570425641, "simple"); + } + + if (x_570425623.doSort) { + x_570425623.kids.sort(HEX3Aanonymous_570425642); + } + + Label1: { + var k_570425667 = null; + var i_570426595 = 0; + var L_570426596 = (x_570425623.kids).length; + Label2: { + Label3: while (true) { + if (!(i_570426595 < L_570426596)) break Label3; + k_570425667 = x_570425623.kids[chckIndx(i_570426595, 0, (x_570425623.kids).length - 1)]; + var y_570425668 = toHtml_570425622(k_570425667, false); + if (!((y_570425668 == null))) { + add_570425492(ul_570425641, tree_570425474("LI", [y_570425668])); + } + + i_570426595 = addInt(i_570426595, 1); + if (!(((x_570425623.kids).length == L_570426596))) { + failedAssertImpl_268435541(makeNimstrLit("iterators.nim(246, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + if (!((ul_570425641.childNodes.length == 0))) { + add_570425492(result_570425625, ul_570425641); + } + + if ((result_570425625.childNodes.length == 0)) { + result_570425625 = null; + } + + }; + + return result_570425625; + +} + +function replaceById_570425502(id_570425503, newTree_570425504) { + var x_570425505 = document.getElementById(id_570425503); + x_570425505.parentNode.replaceChild(newTree_570425504, x_570425505); + newTree_570425504.id = id_570425503; + + +} + +function togglevis_570426052(d_570426053) { + if ((d_570426053.style.display == "none")) { + d_570426053.style.display = "inline"; + } + else { + d_570426053.style.display = "none"; + } + + + +} + +function groupBy(value_570426055) { + var toc_570426056 = document.getElementById("toc-list"); + if ((alternative_570426051[0] == null)) { + var tt_570426064 = {heading: null, kids: [], sortId: 0, doSort: false}; + toToc_570425755(toc_570426056, tt_570426064); + tt_570426064 = tt_570426064.kids[chckIndx(0, 0, (tt_570426064.kids).length - 1)]; + var types_570426069 = [[]]; + var procs_570426074 = [[]]; + extractItems_570425543(tt_570426064, "Types", types_570426069, 0); + extractItems_570425543(tt_570426064, "Procs", procs_570426074, 0); + extractItems_570425543(tt_570426064, "Converters", procs_570426074, 0); + extractItems_570425543(tt_570426064, "Methods", procs_570426074, 0); + extractItems_570425543(tt_570426064, "Templates", procs_570426074, 0); + extractItems_570425543(tt_570426064, "Macros", procs_570426074, 0); + extractItems_570425543(tt_570426064, "Iterators", procs_570426074, 0); + var ntoc_570426075 = buildToc_570425994(tt_570426064, types_570426069[0], procs_570426074[0]); + var x_570426076 = toHtml_570425622(ntoc_570426075, true); + alternative_570426051[0] = tree_570425474("DIV", [x_570426076]); + } + + if ((value_570426055 == "type")) { + replaceById_570425502("tocRoot", alternative_570426051[0]); + } + else { + replaceById_570425502("tocRoot", tree_570425474("DIV", [])); + } + + togglevis_570426052(document.getElementById("toc-list")); + + +} + +function HEX5BHEX5D_738198811(s_738198814, x_738198815) { + var result_738198816 = []; + + var a_738198818 = x_738198815.a; + var L_738198820 = addInt(subInt(subInt((s_738198814).length, x_738198815.b), a_738198818), 1); + result_738198816 = nimCopy(null, mnewString(chckRange(L_738198820, 0, 2147483647)), NTI33554449); + Label1: { + var i_738198825 = 0; + var i_570426605 = 0; + Label2: { + Label3: while (true) { + if (!(i_570426605 < L_738198820)) break Label3; + i_738198825 = i_570426605; + result_738198816[chckIndx(i_738198825, 0, (result_738198816).length - 1)] = s_738198814[chckIndx(addInt(i_738198825, a_738198818), 0, (s_738198814).length - 1)]; + i_570426605 = addInt(i_570426605, 1); + } + }; + }; + + return result_738198816; + +} + +function HEX2EHEX2E_973078632(a_973078635, b_973078636) { + var result_973078639 = ({a: 0, b: 0}); + + result_973078639 = nimCopy(result_973078639, {a: a_973078635, b: b_973078636}, NTI973078613); + + return result_973078639; + +} +async function loadIndex_570426270() { + var result_570426272 = null; + + BeforeRet: { + var indexURL_570426278 = document.getElementById("indexLink").getAttribute("href"); + var rootURL_570426304 = HEX5BHEX5D_738198811(cstrToNimstr(indexURL_570426278), HEX2EHEX2E_973078632(0, 14)); + var resp_570426316 = (await (await fetch(indexURL_570426278)).text()); + var indexElem_570426317 = document.createElement("div"); + indexElem_570426317.innerHTML = resp_570426316; + Label1: { + var href_570426339 = null; + var colontmp__570426599 = []; + colontmp__570426599 = indexElem_570426317.getElementsByClassName("reference"); + var i_570426601 = 0; + var L_570426602 = (colontmp__570426599).length; + Label2: { + Label3: while (true) { + if (!(i_570426601 < L_570426602)) break Label3; + href_570426339 = colontmp__570426599[chckIndx(i_570426601, 0, (colontmp__570426599).length - 1)]; + href_570426339.setAttribute("href", toJSStr((rootURL_570426304 || []).concat(cstrToNimstr(href_570426339.getAttribute("href")) || []))); + db_570426093[0].push(href_570426339);; + contents_570426094[0].push(href_570426339.getAttribute("data-doc-search-tag"));; + i_570426601 = addInt(i_570426601, 1); + if (!(((colontmp__570426599).length == L_570426602))) { + failedAssertImpl_268435541(makeNimstrLit("iterators.nim(246, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + result_570426272 = undefined; + break BeforeRet; + }; + + return result_570426272; + +} + +function then_570426448(future_570426451, onSuccess_570426452, onReject_570426453) { + var result_570426454 = null; + + BeforeRet: { + var ret_570426464 = null; + ret_570426464 = future_570426451.then(onSuccess_570426452, onReject_570426453) + result_570426454 = ret_570426464; + break BeforeRet; + }; + + return result_570426454; + +} + +function nsuToLowerAsciiChar(c_738197589) { + var result_738197590 = 0; + + if ((ConstSet2[c_738197589] != undefined)) { + result_738197590 = (c_738197589 ^ 32); + } + else { + result_738197590 = c_738197589; + } + + + return result_738197590; + +} + +function fuzzyMatch_721420304(pattern_721420305, str_721420306) { + var Temporary4; + var Temporary5; + var Temporary6; + var Temporary7; + var Temporary8; + + var result_721420309 = {Field0: 0, Field1: false}; + + var scoreState_721420310 = (-100); + var headerMatched_721420311 = false; + var unmatchedLeadingCharCount_721420312 = 0; + var consecutiveMatchCount_721420313 = 0; + var strIndex_721420314 = 0; + var patIndex_721420315 = 0; + var score_721420316 = 0; + Label1: { + Label2: while (true) { + if (!((strIndex_721420314 < ((str_721420306) == null ? 0 : (str_721420306).length)) && (patIndex_721420315 < ((pattern_721420305) == null ? 0 : (pattern_721420305).length)))) break Label2; + Label3: { + var patternChar_721420319 = nsuToLowerAsciiChar(pattern_721420305.charCodeAt(chckIndx(patIndex_721420315, 0, (pattern_721420305).length - 1))); + var strChar_721420320 = nsuToLowerAsciiChar(str_721420306.charCodeAt(chckIndx(strIndex_721420314, 0, (str_721420306).length - 1))); + if ((ConstSet3[patternChar_721420319] != undefined)) { + patIndex_721420315 = addInt(patIndex_721420315, 1); + break Label3; + } + + if ((ConstSet4[strChar_721420320] != undefined)) { + strIndex_721420314 = addInt(strIndex_721420314, 1); + break Label3; + } + + if ((!(headerMatched_721420311) && (strChar_721420320 == 58))) { + headerMatched_721420311 = true; + scoreState_721420310 = (-100); + score_721420316 = ((Math.floor((0.5 * score_721420316))) | 0); + patIndex_721420315 = 0; + strIndex_721420314 = addInt(strIndex_721420314, 1); + break Label3; + } + + if ((strChar_721420320 == patternChar_721420319)) { + switch (scoreState_721420310) { + case (-100): + case 20: + scoreState_721420310 = 10; + break; + case 0: + scoreState_721420310 = 5; + score_721420316 = addInt(score_721420316, scoreState_721420310); + break; + case 10: + case 5: + consecutiveMatchCount_721420313 = addInt(consecutiveMatchCount_721420313, 1); + scoreState_721420310 = 5; + score_721420316 = addInt(score_721420316, mulInt(5, consecutiveMatchCount_721420313)); + if ((scoreState_721420310 == 10)) { + score_721420316 = addInt(score_721420316, 10); + } + + var onBoundary_721420372 = (patIndex_721420315 == ((pattern_721420305) == null ? -1 : (pattern_721420305).length - 1)); + if ((!(onBoundary_721420372) && (strIndex_721420314 < ((str_721420306) == null ? -1 : (str_721420306).length - 1)))) { + var nextPatternChar_721420373 = nsuToLowerAsciiChar(pattern_721420305.charCodeAt(chckIndx(addInt(patIndex_721420315, 1), 0, (pattern_721420305).length - 1))); + var nextStrChar_721420374 = nsuToLowerAsciiChar(str_721420306.charCodeAt(chckIndx(addInt(strIndex_721420314, 1), 0, (str_721420306).length - 1))); + if (!!((ConstSet5[nextStrChar_721420374] != undefined))) Temporary4 = false; else { Temporary4 = !((nextStrChar_721420374 == nextPatternChar_721420373)); } onBoundary_721420372 = Temporary4; + } + + if (onBoundary_721420372) { + scoreState_721420310 = 20; + score_721420316 = addInt(score_721420316, scoreState_721420310); + } + + break; + case (-1): + case (-3): + if (!((ConstSet6[str_721420306.charCodeAt(chckIndx(subInt(strIndex_721420314, 1), 0, (str_721420306).length - 1))] != undefined))) Temporary5 = true; else { if (!(ConstSet7[str_721420306.charCodeAt(chckIndx(subInt(strIndex_721420314, 1), 0, (str_721420306).length - 1))] != undefined)) Temporary6 = false; else { Temporary6 = (ConstSet8[str_721420306.charCodeAt(chckIndx(strIndex_721420314, 0, (str_721420306).length - 1))] != undefined); } Temporary5 = Temporary6; } var isLeadingChar_721420398 = Temporary5; + if (isLeadingChar_721420398) { + scoreState_721420310 = 10; + } + else { + scoreState_721420310 = 0; + score_721420316 = addInt(score_721420316, scoreState_721420310); + } + + break; + } + patIndex_721420315 = addInt(patIndex_721420315, 1); + } + else { + switch (scoreState_721420310) { + case (-100): + scoreState_721420310 = (-3); + score_721420316 = addInt(score_721420316, scoreState_721420310); + break; + case 5: + scoreState_721420310 = (-1); + score_721420316 = addInt(score_721420316, scoreState_721420310); + consecutiveMatchCount_721420313 = 0; + break; + case (-3): + if ((unmatchedLeadingCharCount_721420312 < 3)) { + scoreState_721420310 = (-3); + score_721420316 = addInt(score_721420316, scoreState_721420310); + } + + unmatchedLeadingCharCount_721420312 = addInt(unmatchedLeadingCharCount_721420312, 1); + break; + default: + scoreState_721420310 = (-1); + score_721420316 = addInt(score_721420316, scoreState_721420310); + break; + } + } + + strIndex_721420314 = addInt(strIndex_721420314, 1); + }; + } + }; + if (!(patIndex_721420315 == ((pattern_721420305) == null ? 0 : (pattern_721420305).length))) Temporary7 = false; else { if ((strIndex_721420314 == ((str_721420306) == null ? 0 : (str_721420306).length))) Temporary8 = true; else { Temporary8 = !((ConstSet9[str_721420306.charCodeAt(chckIndx(strIndex_721420314, 0, (str_721420306).length - 1))] != undefined)); } Temporary7 = Temporary8; } if (Temporary7) { + score_721420316 = addInt(score_721420316, 10); + } + + var colontmp__570426618 = nimMax(0, score_721420316); + var colontmp__570426619 = (0 < score_721420316); + result_721420309 = nimCopy(result_721420309, {Field0: colontmp__570426618, Field1: colontmp__570426619}, NTI721420302); + + return result_721420309; + +} + +function escapeCString_570426095(x_570426096, x_570426096_Idx) { + var s_570426097 = []; + Label1: { + var c_570426098 = 0; + var iHEX60gensym6_570426622 = 0; + var nHEX60gensym6_570426623 = ((x_570426096[x_570426096_Idx]) == null ? 0 : (x_570426096[x_570426096_Idx]).length); + Label2: { + Label3: while (true) { + if (!(iHEX60gensym6_570426622 < nHEX60gensym6_570426623)) break Label3; + c_570426098 = x_570426096[x_570426096_Idx].charCodeAt(chckIndx(iHEX60gensym6_570426622, 0, (x_570426096[x_570426096_Idx]).length - 1)); + switch (c_570426098) { + case 60: + s_570426097.push.apply(s_570426097, [38,108,116,59]);; + break; + case 62: + s_570426097.push.apply(s_570426097, [38,103,116,59]);; + break; + default: + addChar(s_570426097, c_570426098);; + break; + } + iHEX60gensym6_570426622 = addInt(iHEX60gensym6_570426622, 1); + } + }; + }; + x_570426096[x_570426096_Idx] = toJSStr(s_570426097); + + +} + +function dosearch_570426099(value_570426100) { + +function HEX3Aanonymous_570426127(a_570426132, b_570426133) { + var result_570426138 = 0; + + result_570426138 = subInt(b_570426133["Field1"], a_570426132["Field1"]); + + return result_570426138; + + } + + var result_570426101 = null; + + BeforeRet: { + if (((db_570426093[0]).length == 0)) { + break BeforeRet; + } + + var ul_570426105 = tree_570425474("UL", []); + result_570426101 = tree_570425474("DIV", []); + setClass_570425496(result_570426101, "search_results"); + var matches_570426110 = []; + Label1: { + var i_570426118 = 0; + var colontmp__570426609 = 0; + colontmp__570426609 = (db_570426093[0]).length; + var i_570426610 = 0; + Label2: { + Label3: while (true) { + if (!(i_570426610 < colontmp__570426609)) break Label3; + i_570426118 = i_570426610; + Label4: { + var c_570426119 = contents_570426094[0][chckIndx(i_570426118, 0, (contents_570426094[0]).length - 1)]; + if (((c_570426119 == "Examples") || (c_570426119 == "PEG construction"))) { + break Label4; + } + + var tmpTuple_570426120 = fuzzyMatch_721420304(value_570426100, c_570426119); + var score_570426121 = tmpTuple_570426120["Field0"]; + var matched_570426122 = tmpTuple_570426120["Field1"]; + if (matched_570426122) { + matches_570426110.push({Field0: db_570426093[0][chckIndx(i_570426118, 0, (db_570426093[0]).length - 1)], Field1: score_570426121});; + } + + }; + i_570426610 = addInt(i_570426610, 1); + } + }; + }; + matches_570426110.sort(HEX3Aanonymous_570426127); + Label5: { + var i_570426155 = 0; + var colontmp__570426613 = 0; + colontmp__570426613 = nimMin((matches_570426110).length, 29); + var i_570426614 = 0; + Label6: { + Label7: while (true) { + if (!(i_570426614 < colontmp__570426613)) break Label7; + i_570426155 = i_570426614; + matches_570426110[chckIndx(i_570426155, 0, (matches_570426110).length - 1)]["Field0"].innerHTML = matches_570426110[chckIndx(i_570426155, 0, (matches_570426110).length - 1)]["Field0"].getAttribute("data-doc-search-tag"); + escapeCString_570426095(matches_570426110[chckIndx(i_570426155, 0, (matches_570426110).length - 1)]["Field0"], "innerHTML"); + add_570425492(ul_570426105, tree_570425474("LI", [matches_570426110[chckIndx(i_570426155, 0, (matches_570426110).length - 1)]["Field0"]])); + i_570426614 = addInt(i_570426614, 1); + } + }; + }; + if ((ul_570426105.childNodes.length == 0)) { + add_570425492(result_570426101, tree_570425474("B", [text_570425499("no search results")])); + } + else { + add_570425492(result_570426101, tree_570425474("B", [text_570425499("search results")])); + add_570425492(result_570426101, ul_570426105); + } + + }; + + return result_570426101; + +} + +function search() { + +function wrapper_570426433() { + var elem_570426434 = document.getElementById("searchInput"); + var value_570426435 = elem_570426434.value; + if (!((((value_570426435) == null ? 0 : (value_570426435).length) == 0))) { + if ((oldtoc_570426428[0] == null)) { + oldtoc_570426428[0] = document.getElementById("tocRoot"); + } + + var results_570426439 = dosearch_570426099(value_570426435); + replaceById_570425502("tocRoot", results_570426439); + } + else { + if (!((oldtoc_570426428[0] == null))) { + replaceById_570425502("tocRoot", oldtoc_570426428[0]); + } + } + + + } + + if ((loadIndexFut_570426431[0] == null)) { + loadIndexFut_570426431[0] = loadIndex_570426270(); + var _ = then_570426448(loadIndexFut_570426431[0], wrapper_570426433, null); + } + + if (!((timer_570426429[0] == null))) { + clearTimeout(timer_570426429[0]); + } + + timer_570426429[0] = setTimeout(wrapper_570426433, 400); + + +} + +function copyToClipboard() { + + function updatePreTags() { + + const allPreTags = document.querySelectorAll("pre") + + allPreTags.forEach((e) => { + + const div = document.createElement("div") + div.classList.add("copyToClipBoard") + + const preTag = document.createElement("pre") + preTag.innerHTML = e.innerHTML + + const button = document.createElement("button") + button.value = e.textContent.replace('...', '') + button.classList.add("copyToClipBoardBtn") + button.style.cursor = "pointer" + + div.appendChild(preTag) + div.appendChild(button) + + e.outerHTML = div.outerHTML + + }) + } + + + function copyTextToClipboard(e) { + const clipBoardContent = e.target.value + navigator.clipboard.writeText(clipBoardContent).then(function() { + e.target.style.setProperty("--clipboard-image", "var(--clipboard-image-selected)") + }, function(err) { + console.error("Could not copy text: ", err); + }); + } + + window.addEventListener("click", (e) => { + if (e.target.classList.contains("copyToClipBoardBtn")) { + copyTextToClipboard(e) + } + }) + + window.addEventListener("mouseover", (e) => { + if (e.target.nodeName === "PRE") { + e.target.nextElementSibling.style.setProperty("--clipboard-image", "var(--clipboard-image-normal)") + } + }) + + window.addEventListener("DOMContentLoaded", updatePreTags) + + + + +} +var Temporary1; +var t_570425383 = window.localStorage.getItem("theme"); +if ((t_570425383 == null)) { +Temporary1 = "auto"; +} +else { +Temporary1 = t_570425383; +} + +setTheme(Temporary1); +var alternative_570426051 = [null]; +var db_570426093 = [[]]; +var contents_570426094 = [[]]; +var oldtoc_570426428 = [null]; +var timer_570426429 = [null]; +var loadIndexFut_570426431 = [null]; +copyToClipboard(); +window.addEventListener("DOMContentLoaded", onDOMLoaded, false); diff --git a/clients/nim-allographer/docs/env.html b/clients/nim-allographer/docs/env.html new file mode 100644 index 0000000..6581aee --- /dev/null +++ b/clients/nim-allographer/docs/env.html @@ -0,0 +1,134 @@ + + + + + + + +src/allographer/env + + + + + + + + + + + + +
+
+

src/allographer/env

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Consts

+
+
+
isExistsMariadb = true
+
+ + + +
+
+
+
isExistsMysql = true
+
+ + + +
+
+
+
isExistsPostgres = true
+
+ + + +
+
+
+
isExistsSqlite = true
+
+ + + +
+
+
+
isExistsSurrealdb = true
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+ + + + + + diff --git a/clients/nim-allographer/docs/env.idx b/clients/nim-allographer/docs/env.idx new file mode 100644 index 0000000..f536a1e --- /dev/null +++ b/clients/nim-allographer/docs/env.idx @@ -0,0 +1,6 @@ +nimTitle env env.html module src/allographer/env 0 +nim isExistsSqlite env.html#isExistsSqlite const isExistsSqlite 22 +nim isExistsPostgres env.html#isExistsPostgres const isExistsPostgres 23 +nim isExistsMysql env.html#isExistsMysql const isExistsMysql 24 +nim isExistsMariadb env.html#isExistsMariadb const isExistsMariadb 25 +nim isExistsSurrealdb env.html#isExistsSurrealdb const isExistsSurrealdb 26 diff --git a/clients/nim-allographer/docs/nimdoc.out.css b/clients/nim-allographer/docs/nimdoc.out.css new file mode 100644 index 0000000..1417d9e --- /dev/null +++ b/clients/nim-allographer/docs/nimdoc.out.css @@ -0,0 +1,1026 @@ +/* +Stylesheet for use with Docutils/rst2html. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. + +Modified from Chad Skeeters' rst2html-style +https://bitbucket.org/cskeeters/rst2html-style/ + +Modified by Boyd Greenfield and narimiran +*/ + +:root { + --primary-background: #fff; + --secondary-background: ghostwhite; + --third-background: #e8e8e8; + --info-background: #50c050; + --warning-background: #c0a000; + --error-background: #e04040; + --border: #dde; + --text: #222; + --anchor: #07b; + --anchor-focus: #607c9f; + --input-focus: #1fa0eb; + --strong: #3c3c3c; + --hint: #9A9A9A; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1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"); + + --keyword: #5e8f60; + --identifier: #222; + --comment: #484a86; + --operator: #155da4; + --punctuation: black; + --other: black; + --escapeSequence: #c4891b; + --number: #252dbe; + --literal: #a4255b; + --program: #6060c0; + --option: #508000; + --raw-data: #a4255b; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal) +} + +[data-theme="dark"] { + --primary-background: #171921; + --secondary-background: #1e202a; + --third-background: #2b2e3b; + --info-background: #008000; + --warning-background: #807000; + --error-background: #c03000; + --border: #0e1014; + --text: #fff; + --anchor: #8be9fd; + --anchor-focus: #8be9fd; + --input-focus: #8be9fd; + --strong: #bd93f9; + --hint: #7A7C85; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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"); + + --keyword: #ff79c6; + --identifier: #f8f8f2; + --comment: #6272a4; + --operator: #ff79c6; + --punctuation: #f8f8f2; + --other: #f8f8f2; + --escapeSequence: #bd93f9; + --number: #bd93f9; + --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; + --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); +} + +@media (prefers-color-scheme: dark) { + [data-theme="auto"] { + --primary-background: #171921; + --secondary-background: #1e202a; + --third-background: #2b2e3b; + --info-background: #008000; + --warning-background: #807000; + --error-background: #c03000; + --border: #0e1014; + --text: #fff; + --anchor: #8be9fd; + --anchor-focus: #8be9fd; + --input-focus: #8be9fd; + --strong: #bd93f9; + --hint: #7A7C85; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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"); + + --keyword: #ff79c6; + --identifier: #f8f8f2; + --comment: #6272a4; + --operator: #ff79c6; + --punctuation: #f8f8f2; + --other: #f8f8f2; + --escapeSequence: #bd93f9; + --number: #bd93f9; + --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; + --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); + } +} + +.theme-select-wrapper { + display: flex; + align-items: center; +} + +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; } + +body { + font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; + font-weight: 400; + font-size: 1.125em; + line-height: 1.5; + color: var(--text); + background-color: var(--primary-background); } + +/* Skeleton grid */ +.container { + position: relative; + width: 100%; + max-width: 1050px; + margin: 0 auto; + padding: 0; + box-sizing: border-box; } + +.column, .columns { + width: 100%; + float: left; + box-sizing: border-box; + margin-left: 1%; } + +@media print { + #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby { + display:none; + } + .columns { + width:100% !important; + } +} + +.column:first-child, .columns:first-child { + margin-left: 0; } + +.container .row { + display: flex; } + +.three.columns { + width: 25.0%; + height: 100vh; + position: sticky; + top: 0px; + overflow-y: auto; + padding: 2px; +} + +.nine.columns { + width: 75.0%; + padding-left: 1.5em; } + +.twelve.columns { + width: 100%; + margin-left: 0; } + +@media screen and (max-width: 860px) { + .three.columns { + display: none; + } + .nine.columns { + width: 98.0%; + } + body { + font-size: 1em; + line-height: 1.35; + } +} + +cite { + font-style: italic !important; } + + +/* Nim search input */ +div#searchInputDiv { + margin-bottom: 1em; +} +input#searchInput { + width: 80%; +} + +/* + * Some custom formatting for input forms. + * This also fixes input form colors on Firefox with a dark system theme on Linux. + */ +input { + -moz-appearance: none; + background-color: var(--secondary-background); + color: var(--text); + border: 1px solid var(--border); + font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; + font-size: 0.9em; + padding: 6px; +} + +input:focus { + border: 1px solid var(--input-focus); + box-shadow: 0 0 3px var(--input-focus); +} + +select { + -moz-appearance: none; + background-color: var(--secondary-background); + color: var(--text); + border: 1px solid var(--border); + font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; + font-size: 0.9em; + padding: 6px; +} + +select:focus { + border: 1px solid var(--input-focus); + box-shadow: 0 0 3px var(--input-focus); +} + +/* Docgen styles */ + +:target { + border: 2px solid #B5651D; + border-style: dotted; +} + +/* Links */ +a { + color: var(--anchor); + text-decoration: none; +} + +a span.Identifier { + text-decoration: underline; + text-decoration-color: #aab; +} + +a.reference-toplevel { + font-weight: bold; +} + +a.nimdoc { + word-spacing: 0.3em; +} + +a.toc-backref { + text-decoration: none; + color: var(--text); +} + +a.link-seesrc { + color: #607c9f; + font-size: 0.9em; + font-style: italic; +} + +a:hover, a:focus { + color: var(--anchor-focus); + text-decoration: underline; +} + +a:hover span.Identifier { + color: var(--anchor); +} + + +sub, sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; } + +sup { + top: -0.5em; } + +sub { + bottom: -0.25em; } + +img { + width: auto; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; } + +@media print { + * { + color: black !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; } + + a, a:visited { + text-decoration: underline; } + + a[href]:after { + content: " (" attr(href) ")"; } + + abbr[title]:after { + content: " (" attr(title) ")"; } + + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; } + + pre, blockquote { + border: 1px solid #999; + page-break-inside: avoid; } + + thead { + display: table-header-group; } + + tr, img { + page-break-inside: avoid; } + + img { + max-width: 100% !important; } + + @page { + margin: 0.5cm; } + + h1 { + page-break-before: always; } + + h1.title { + page-break-before: avoid; } + + p, h2, h3 { + orphans: 3; + widows: 3; } + + h2, h3 { + page-break-after: avoid; } +} + + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; } + +small { + font-size: 85%; } + +strong { + font-weight: 600; + font-size: 0.95em; + color: var(--strong); } + +em { + font-style: italic; } + +h1 { + font-size: 1.8em; + font-weight: 400; + padding-bottom: .25em; + border-bottom: 6px solid var(--third-background); + margin-top: 2.5em; + margin-bottom: 1em; + line-height: 1.2em; } + +h1.title { + padding-bottom: 1em; + border-bottom: 0px; + font-size: 2.5em; + text-align: center; + font-weight: 900; + margin-top: 0.75em; + margin-bottom: 0em; } + +h2 { + font-size: 1.3em; + margin-top: 2em; } + +h2.subtitle { + margin-top: 0em; + text-align: center; } + +h3 { + font-size: 1.125em; + font-style: italic; + margin-top: 1.5em; } + +h4 { + font-size: 1.125em; + margin-top: 1em; } + +h5 { + font-size: 1.125em; + margin-top: 0.75em; } + +h6 { + font-size: 1.1em; } + + +ul, ol { + padding: 0; + margin-top: 0.5em; + margin-left: 0.75em; } + +ul ul, ul ol, ol ol, ol ul { + margin-bottom: 0; + margin-left: 1.25em; } + +ul.simple > li { + list-style-type: circle; } + +ul.simple-boot li { + list-style-type: none; + margin-left: 0em; + margin-bottom: 0.5em; } + +ol.simple > li, ul.simple > li { + margin-bottom: 0.2em; + margin-left: 0.4em } + +ul.simple.simple-toc > li { + margin-top: 1em; } + +ul.simple-toc { + list-style: none; + font-size: 0.9em; + margin-left: -0.3em; + margin-top: 1em; } + +ul.simple-toc > li { + list-style-type: none; } + +ul.simple-toc-section { + list-style-type: circle; + margin-left: 0.8em; + color: #6c9aae; } + +ul.nested-toc-section { + list-style-type: circle; + margin-left: -0.75em; + color: var(--text); } + +ul.nested-toc-section > li { + margin-left: 1.25em; } + + +ol.arabic { + list-style: decimal; } + +ol.loweralpha { + list-style: lower-alpha; } + +ol.upperalpha { + list-style: upper-alpha; } + +ol.lowerroman { + list-style: lower-roman; } + +ol.upperroman { + list-style: upper-roman; } + +ul.auto-toc { + list-style-type: none; } + + +dl { + margin-bottom: 1.5em; } + +dt { + margin-bottom: -0.5em; + margin-left: 0.0em; } + +dd { + margin-left: 2.0em; + margin-bottom: 3.0em; + margin-top: 0.5em; } + + +hr { + margin: 2em 0; + border: 0; + border-top: 1px solid #aaa; } + +hr.footnote { + width: 25%; + border-top: 0.15em solid #999; + margin-bottom: 0.15em; + margin-top: 0.15em; +} +div.footnote-group { + margin-left: 1em; +} +div.footnote-label { + display: inline-block; + min-width: 1.7em; +} + +div.option-list { + border: 0.1em solid var(--border); +} +div.option-list-item { + padding-left: 12em; + padding-right: 0; + padding-bottom: 0.3em; + padding-top: 0.3em; +} +div.odd { + background-color: var(--secondary-background); +} +div.option-list-label { + margin-left: -11.5em; + margin-right: 0em; + min-width: 11.5em; + display: inline-block; + vertical-align: top; +} +div.option-list-description { + width: calc(100% - 1em); + padding-left: 1em; + padding-right: 0; + display: inline-block; +} + +blockquote { + font-size: 0.9em; + font-style: italic; + padding-left: 0.5em; + margin-left: 0; + border-left: 5px solid #bbc; +} + +blockquote.markdown-quote { + font-size: 0.9rem; /* use rem to avoid recursion */ + font-style: normal; +} + +.pre, span.tok { + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + font-weight: 500; + font-size: 0.85em; + color: var(--text); + background-color: var(--third-background); + padding-left: 3px; + padding-right: 3px; + border-radius: 4px; +} + +span.tok { + border: 1px solid #808080; + padding-bottom: 0.1em; + margin-right: 0.2em; +} + +.copyToClipBoard { + position: relative; +} + +pre { + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--text); + font-weight: 500; + display: inline-block; + box-sizing: border-box; + min-width: 100%; + padding: 0.5em; + margin-top: 0.5em; + margin-bottom: 0.5em; + font-size: 0.85em; + white-space: pre !important; + overflow-y: hidden; + overflow-x: visible; + background-color: var(--secondary-background); + border: 1px solid var(--border); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.copyToClipBoardBtn { + visibility: hidden; + position: absolute; + width: 24px; + border-radius: 4px; + background-image: var(--clipboard-image); + right: 5px; + top: 13px; + background-color: var(--secondary-background); + padding: 11px; + border: 0; +} + +.copyToClipBoard:hover .copyToClipBoardBtn { + visibility: visible; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; } + + +/* Nim line-numbered tables */ +.line-nums-table { + width: 100%; + table-layout: fixed; } + +table.line-nums-table { + border-radius: 4px; + border: 1px solid #cccccc; + background-color: ghostwhite; + border-collapse: separate; + margin-top: 15px; + margin-bottom: 25px; } + +.line-nums-table tbody { + border: none; } + +.line-nums-table td pre { + border: none; + background-color: transparent; } + +.line-nums-table td.blob-line-nums { + width: 28px; } + +.line-nums-table td.blob-line-nums pre { + color: #b0b0b0; + -webkit-filter: opacity(75%); + filter: opacity(75%); + text-align: right; + border-color: transparent; + background-color: transparent; + padding-left: 0px; + margin-left: 0px; + padding-right: 0px; + margin-right: 0px; } + + +table { + max-width: 100%; + background-color: transparent; + margin-top: 0.5em; + margin-bottom: 1.5em; + border-collapse: collapse; + border-color: var(--third-background); + border-spacing: 0; + font-size: 0.9em; +} + +table th, table td { + padding: 0px 0.5em 0px; + border-color: var(--third-background); +} + +table th { + background-color: var(--third-background); + border-color: var(--third-background); + font-weight: bold; } + +table th.docinfo-name { + background-color: transparent; + text-align: right; +} + +table tr:hover { + background-color: var(--third-background); } + + +/* rst2html default used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0; } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 !important; } + +.admonition { + padding: 0.3em; + background-color: var(--secondary-background); + border-left: 0.4em solid #7f7f84; + margin-bottom: 0.5em; + -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); +} +.admonition-info { + border-color: var(--info-background); +} +.admonition-info-text { + color: var(--info-background); +} +.admonition-warning { + border-color: var(--warning-background); +} +.admonition-warning-text { + color: var(--warning-background); +} +.admonition-error { + border-color: var(--error-background); +} +.admonition-error-text { + color: var(--error-background); +} + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 !important; } + +.last, .with-subtitle { + margin-bottom: 0 !important; } + +.hidden { + display: none; } + +blockquote.epigraph { + margin: 2em 5em; } + +dl.docutils dd { + margin-bottom: 0.5em; } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; } + + +div.figure { + margin-left: 2em; + margin-right: 2em; } + +div.footer, div.header { + clear: both; + text-align: center; + color: #666; + font-size: smaller; } + +div.footer { + padding-top: 5em; } + +div.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; } + +div.line-block div.line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; } + +div.topic { + margin: 2em; } + +div.search_results { + background-color: var(--third-background); + margin: 3em; + padding: 1em; + border: 1px solid #4d4d4d; } + +div#global-links ul { + margin-left: 0; + list-style-type: none; } + +div#global-links > simple-boot { + margin-left: 3em; } + +hr.docutils { + width: 75%; } + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; } + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; } + +.align-left { + text-align: left; } + +.align-center { + clear: both; + text-align: center; } + +.align-right { + text-align: right; } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit; } + +p.attribution { + text-align: right; + margin-left: 50%; } + +p.caption { + font-style: italic; } + +p.credits { + font-style: italic; + font-size: smaller; } + +p.label { + white-space: nowrap; } + +p.rubric { + font-weight: bold; + font-size: larger; + color: maroon; + text-align: center; } + +p.topic-title { + font-weight: bold; } + +pre.address { + margin-bottom: 0; + margin-top: 0; + font: inherit; } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em; + margin-right: 2em; } + +pre.code .ln { + color: grey; } + +/* line numbers */ +pre.code, code { + background-color: #eeeeee; } + +pre.code .comment, code .comment { + color: #5c6576; } + +pre.code .keyword, code .keyword { + color: #3B0D06; + font-weight: bold; } + +pre.code .literal.string, code .literal.string { + color: #0c5404; } + +pre.code .name.builtin, code .name.builtin { + color: #352b84; } + +pre.code .deleted, code .deleted { + background-color: #DEB0A1; } + +pre.code .inserted, code .inserted { + background-color: #A3D289; } + +span.classifier { + font-style: oblique; } + +span.classifier-delimiter { + font-weight: bold; } + +span.problematic { + color: #b30000; } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80%; } + +span.DecNumber { + color: var(--number); } + +span.BinNumber { + color: var(--number); } + +span.HexNumber { + color: var(--number); } + +span.OctNumber { + color: var(--number); } + +span.FloatNumber { + color: var(--number); } + +span.Identifier { + color: var(--identifier); } + +span.Keyword { + font-weight: 600; + color: var(--keyword); } + +span.StringLit { + color: var(--literal); } + +span.LongStringLit { + color: var(--literal); } + +span.CharLit { + color: var(--literal); } + +span.EscapeSequence { + color: var(--escapeSequence); } + +span.Operator { + color: var(--operator); } + +span.Punctuation { + color: var(--punctuation); } + +span.Comment, span.LongComment { + font-style: italic; + font-weight: 400; + color: var(--comment); } + +span.RegularExpression { + color: darkviolet; } + +span.TagStart { + color: darkviolet; } + +span.TagEnd { + color: darkviolet; } + +span.Key { + color: #252dbe; } + +span.Value { + color: #252dbe; } + +span.RawData { + color: var(--raw-data); } + +span.Assembler { + color: #252dbe; } + +span.Preprocessor { + color: #252dbe; } + +span.Directive { + color: #252dbe; } + +span.option { + font-weight: bold; + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--option); } + +span.Prompt { + font-weight: bold; + color: red; } + +span.ProgramOutput { + font-weight: bold; + color: #808080; } + +span.program { + font-weight: bold; + color: var(--program); + text-decoration: underline; + text-decoration-color: var(--hint); + text-decoration-thickness: 0.05em; + text-underline-offset: 0.15em; } + +span.Command, span.Rule, span.Hyperlink, +span.Label, span.Reference, span.Other { + color: var(--other); } + +/* Pop type, const, proc, and iterator defs in nim def blocks */ +dt pre > span.Identifier, dt pre > span.Operator { + color: var(--identifier); + font-weight: 700; } + +dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier, +dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier { + color: var(--identifier); + font-weight: inherit; } + +/* Nim sprite for the footer (taken from main page favicon) */ +.nim-sprite { + display: inline-block; + width: 51px; + height: 14px; + background-position: 0 0; + background-size: 51px 14px; + -webkit-filter: opacity(50%); + filter: opacity(50%); + background-repeat: no-repeat; + background-image: var(--nim-sprite-base64); + margin-bottom: 5px; } + +span.pragmadots { + /* Position: relative frees us up to make the dots + look really nice without fucking up the layout and + causing bulging in the parent container */ + position: relative; + /* 1px down looks slightly nicer */ + top: 1px; + padding: 2px; + background-color: var(--third-background); + border-radius: 4px; + margin: 0 2px; + cursor: pointer; + font-size: 0.8em; } + +span.pragmadots:hover { + background-color: var(--hint); } + +span.pragmawrap { + display: none; } + +span.attachedType { + display: none; + visibility: hidden; } diff --git a/clients/nim-allographer/docs/query_builder.html b/clients/nim-allographer/docs/query_builder.html new file mode 100644 index 0000000..464ed08 --- /dev/null +++ b/clients/nim-allographer/docs/query_builder.html @@ -0,0 +1,93 @@ + + + + + + + +src/allographer/query_builder + + + + + + + + + + + + +
+
+

src/allographer/query_builder

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Exports

+
+ Numeric, None, Order, Collate, Asc, Desc, Collation, DbError, dbError, orm, orm, $, Connections, RawSqliteQuery, SqliteQuery, Connection, SqliteConnections, SQLite3, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, min, firstPlain, max, findPlain, first, sum, exec, count, rollback, update, delete, insertId, delete, get, getPlain, getPlain, insertId, columns, insert, begin, get, firstPlain, first, seeder, avg, findPlain, seeder, find, commit, insert, find, transaction, $, RawPostgresQuery, PostgreSQL, Connections, PostgresQuery, Connection, PostgresConnections, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, min, firstPlain, max, findPlain, first, sum, exec, count, rollback, update, delete, insertId, delete, get, getPlain, getPlain, insertId, columns, insert, begin, get, firstPlain, first, seeder, avg, findPlain, seeder, find, commit, insert, find, transaction, $, MariaDB, Connections, ConnectionInfo, MariadbQuery, RawMariadbQuery, MariadbConnections, Connection, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, min, firstPlain, max, findPlain, first, sum, exec, count, rollback, update, delete, insertId, delete, get, getPlain, getPlain, insertId, columns, insert, begin, get, firstPlain, first, seeder, avg, findPlain, seeder, find, commit, insert, find, transaction, $, MySQL, MysqlConnections, Connections, RawMysqlQuery, ConnectionInfo, MysqlQuery, Connection, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, min, firstPlain, max, findPlain, first, sum, exec, count, rollback, update, delete, insertId, delete, get, getPlain, getPlain, insertId, columns, insert, begin, get, firstPlain, first, seeder, avg, findPlain, seeder, find, commit, insert, find, transaction, $, $, new, rawId, new, SurrealId, new, RawSurrealQuery, SurrealDB, %, Connections, Connection, new, SurrealQuery, SurrealConnections, distinct, limit, parallel, having, table, whereNull, whereIn, groupBy, select, whereBetween, orderBy, fetch, raw, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, start, min, max, first, sum, exec, count, update, delete, get, insertId, columns, insertId, get, insert, first, update, info, seeder, avg, delete, seeder, insert, find +
+
+ +
+
+ + +
+
+ + + + + + diff --git a/clients/nim-allographer/docs/query_builder.idx b/clients/nim-allographer/docs/query_builder.idx new file mode 100644 index 0000000..c9deaf4 --- /dev/null +++ b/clients/nim-allographer/docs/query_builder.idx @@ -0,0 +1 @@ +nimTitle query_builder query_builder.html module src/allographer/query_builder 0 diff --git a/clients/nim-allographer/docs/schema_builder.html b/clients/nim-allographer/docs/schema_builder.html new file mode 100644 index 0000000..238bc81 --- /dev/null +++ b/clients/nim-allographer/docs/schema_builder.html @@ -0,0 +1,93 @@ + + + + + + + +src/allographer/schema_builder + + + + + + + + + + + + +
+
+

src/allographer/schema_builder

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ + + +
+
+ + +
+
+ + + + + + diff --git a/clients/nim-allographer/docs/schema_builder.idx b/clients/nim-allographer/docs/schema_builder.idx new file mode 100644 index 0000000..3830560 --- /dev/null +++ b/clients/nim-allographer/docs/schema_builder.idx @@ -0,0 +1 @@ +nimTitle schema_builder schema_builder.html module src/allographer/schema_builder 0 diff --git a/clients/nim-allographer/docs/theindex.html b/clients/nim-allographer/docs/theindex.html new file mode 100644 index 0000000..1f228b6 --- /dev/null +++ b/clients/nim-allographer/docs/theindex.html @@ -0,0 +1,7804 @@ + + + + + + + +Index + + + + + + + + + + + + +
+
+

Index

+ Modules: connection, env, query_builder, schema_builder, utils, v2/query_builder/enums, v2/query_builder/error, v2/query_builder/libs/mariadb/mariadb_impl, v2/query_builder/libs/mariadb/mariadb_lib, v2/query_builder/libs/mariadb/mariadb_rdb, v2/query_builder/libs/mysql/mysql_impl, v2/query_builder/libs/mysql/mysql_lib, v2/query_builder/libs/mysql/mysql_rdb, v2/query_builder/libs/postgres/postgres_impl, v2/query_builder/libs/postgres/postgres_lib, v2/query_builder/libs/postgres/postgres_rdb, v2/query_builder/libs/sqlite/sqlite_impl, v2/query_builder/libs/sqlite/sqlite_lib, v2/query_builder/libs/sqlite/sqlite_rdb, v2/query_builder/libs/surreal/surreal_impl, v2/query_builder/libs/surreal/surreal_lib, v2/query_builder/libs/surreal/surreal_rdb, v2/query_builder/log, v2/query_builder/models/database_types, v2/query_builder/models/mariadb/mariadb_exec, v2/query_builder/models/mariadb/mariadb_open, v2/query_builder/models/mariadb/mariadb_query, v2/query_builder/models/mariadb/mariadb_transaction, v2/query_builder/models/mariadb/mariadb_types, v2/query_builder/models/mariadb/query/mariadb_builder, v2/query_builder/models/mariadb/query/mariadb_generator, v2/query_builder/models/mysql/mysql_exec, v2/query_builder/models/mysql/mysql_open, v2/query_builder/models/mysql/mysql_query, v2/query_builder/models/mysql/mysql_transaction, v2/query_builder/models/mysql/mysql_types, v2/query_builder/models/mysql/query/mysql_builder, v2/query_builder/models/mysql/query/mysql_generator, v2/query_builder/models/orm, v2/query_builder/models/postgres/poatgres_transaction, v2/query_builder/models/postgres/postgres_exec, v2/query_builder/models/postgres/postgres_open, v2/query_builder/models/postgres/postgres_query, v2/query_builder/models/postgres/postgres_types, v2/query_builder/models/postgres/query/postgres_builder, v2/query_builder/models/postgres/query/postgres_generator, v2/query_builder/models/sqlite/query/sqlite_builder, v2/query_builder/models/sqlite/query/sqlite_generator, v2/query_builder/models/sqlite/sqlite_exec, v2/query_builder/models/sqlite/sqlite_open, v2/query_builder/models/sqlite/sqlite_query, v2/query_builder/models/sqlite/sqlite_transaction, v2/query_builder/models/sqlite/sqlite_types, v2/query_builder/models/surreal/query/surreal_builder, v2/query_builder/models/surreal/query/surreal_generator, v2/query_builder/models/surreal/surreal_exec, v2/query_builder/models/surreal/surreal_open, v2/query_builder/models/surreal/surreal_query, v2/query_builder/models/surreal/surreal_types, v2/schema_builder/enums, v2/schema_builder/models/column, v2/schema_builder/models/table, v2/schema_builder/queries/mariadb/add_column, v2/schema_builder/queries/mariadb/change_column, v2/schema_builder/queries/mariadb/create_migration_table, v2/schema_builder/queries/mariadb/create_table, v2/schema_builder/queries/mariadb/drop_column, v2/schema_builder/queries/mariadb/drop_table, v2/schema_builder/queries/mariadb/mariadb_query_impl, v2/schema_builder/queries/mariadb/mariadb_query_type, v2/schema_builder/queries/mariadb/rename_column, v2/schema_builder/queries/mariadb/rename_table, v2/schema_builder/queries/mariadb/reset_table, v2/schema_builder/queries/mariadb/schema_utils, v2/schema_builder/queries/mariadb/sub/add_column_query, v2/schema_builder/queries/mariadb/sub/change_column_query, v2/schema_builder/queries/mariadb/sub/create_column_query, v2/schema_builder/queries/mysql/add_column, v2/schema_builder/queries/mysql/change_column, v2/schema_builder/queries/mysql/create_migration_table, v2/schema_builder/queries/mysql/create_table, v2/schema_builder/queries/mysql/drop_column, v2/schema_builder/queries/mysql/drop_table, v2/schema_builder/queries/mysql/mysql_query_impl, v2/schema_builder/queries/mysql/mysql_query_type, v2/schema_builder/queries/mysql/rename_column, v2/schema_builder/queries/mysql/rename_table, v2/schema_builder/queries/mysql/reset_table, v2/schema_builder/queries/mysql/schema_utils, v2/schema_builder/queries/mysql/sub/add_column_query, v2/schema_builder/queries/mysql/sub/change_column_query, v2/schema_builder/queries/mysql/sub/create_column_query, v2/schema_builder/queries/mysql/sub/is_exists, v2/schema_builder/queries/postgres/add_column, v2/schema_builder/queries/postgres/change_column, v2/schema_builder/queries/postgres/create_migration_table, v2/schema_builder/queries/postgres/create_table, v2/schema_builder/queries/postgres/drop_column, v2/schema_builder/queries/postgres/drop_table, v2/schema_builder/queries/postgres/postgres_query_impl, v2/schema_builder/queries/postgres/postgres_query_type, v2/schema_builder/queries/postgres/rename_column, v2/schema_builder/queries/postgres/rename_table, v2/schema_builder/queries/postgres/reset_table, v2/schema_builder/queries/postgres/schema_utils, v2/schema_builder/queries/postgres/sub/add_column_query, v2/schema_builder/queries/postgres/sub/change_column_query, v2/schema_builder/queries/postgres/sub/create_column_query, v2/schema_builder/queries/schema_interface, v2/schema_builder/queries/sqlite/add_column, v2/schema_builder/queries/sqlite/change_column, v2/schema_builder/queries/sqlite/create_migration_table, v2/schema_builder/queries/sqlite/create_table, v2/schema_builder/queries/sqlite/drop_column, v2/schema_builder/queries/sqlite/drop_table, v2/schema_builder/queries/sqlite/rename_column, v2/schema_builder/queries/sqlite/rename_table, v2/schema_builder/queries/sqlite/reset_table, v2/schema_builder/queries/sqlite/schema_utils, v2/schema_builder/queries/sqlite/sqlite_query_impl, v2/schema_builder/queries/sqlite/sqlite_query_type, v2/schema_builder/queries/sqlite/sub/add_column_query, v2/schema_builder/queries/sqlite/sub/create_column_query, v2/schema_builder/queries/surreal/add_column, v2/schema_builder/queries/surreal/change_column, v2/schema_builder/queries/surreal/create_migration_table, v2/schema_builder/queries/surreal/create_sequence_table, v2/schema_builder/queries/surreal/create_table, v2/schema_builder/queries/surreal/drop_column, v2/schema_builder/queries/surreal/drop_table, v2/schema_builder/queries/surreal/rename_column, v2/schema_builder/queries/surreal/rename_table, v2/schema_builder/queries/surreal/reset_table, v2/schema_builder/queries/surreal/schema_utils, v2/schema_builder/queries/surreal/sub/create_column_query, v2/schema_builder/queries/surreal/surreal_query_impl, v2/schema_builder/queries/surreal/surreal_query_type, v2/schema_builder/usecases/mariadb/alter, v2/schema_builder/usecases/mariadb/create, v2/schema_builder/usecases/mariadb/create_query_def, v2/schema_builder/usecases/mariadb/drop, v2/schema_builder/usecases/mysql/alter, v2/schema_builder/usecases/mysql/create, v2/schema_builder/usecases/mysql/create_query_def, v2/schema_builder/usecases/mysql/drop, v2/schema_builder/usecases/postgres/alter, v2/schema_builder/usecases/postgres/create, v2/schema_builder/usecases/postgres/create_query_def, v2/schema_builder/usecases/postgres/drop, v2/schema_builder/usecases/sqlite/alter, v2/schema_builder/usecases/sqlite/create, v2/schema_builder/usecases/sqlite/create_query_def, v2/schema_builder/usecases/sqlite/drop, v2/schema_builder/usecases/sub/migration_table_def, v2/schema_builder/usecases/surreal/alter, v2/schema_builder/usecases/surreal/create, v2/schema_builder/usecases/surreal/create_query_def, v2/schema_builder/usecases/surreal/drop.

API symbols

+
`$`:
+
`%`:
+
`[]`:
+
`distinct`:
+
add:
+
AddColumn:
+
addColumn:
+
addColumnString:
+
addIndexString:
+
add_slave:
+
affected_rows:
+
aggregate_context:
+
aggregate_count:
+
ALLOC_MAX_BLOCK_TO_DROP:
+
ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP:
+
Alter:
+
alter:
+
alterAddForeignKey:
+
Asc:
+
autocommit:
+
autoIncrement:
+
AUTO_INCREMENT_FLAG:
+
avg:
+
avgBuilder:
+
backup_finish:
+
backup_init:
+
backup_pagecount:
+
backup_remaining:
+
backup_step:
+
begin:
+
bigInteger:
+
binary:
+
BINARY_FLAG:
+
BINCMP_FLAG:
+
BIND:
+
bind_blob:
+
bind_double:
+
bind_int:
+
bind_int64:
+
bind_null:
+
bindParam:
+
bind_parameter_count:
+
bind_parameter_index:
+
bind_parameter_name:
+
bind_text:
+
bind_text16:
+
BLOB_FLAG:
+
boolean:
+
busy_handler:
+
busy_timeout:
+
Callback:
+
CASCADE:
+
change:
+
ChangeColumn:
+
changeColumn:
+
changeColumnString:
+
changeForeignKey:
+
changeIndexString:
+
changes:
+
ChangeTable:
+
change_user:
+
char:
+
Character_set:
+
character_set_name:
+
CHARSET_INFO:
+
Charset_info_st:
+
checkError:
+
check_scramble:
+
check_scramble_323:
+
clear_bindings:
+
CLIENT_COMPRESS:
+
CLIENT_CONNECT_WITH_DB:
+
CLIENT_FOUND_ROWS:
+
CLIENT_IGNORE_SIGPIPE:
+
CLIENT_IGNORE_SPACE:
+
CLIENT_INTERACTIVE:
+
CLIENT_LOCAL_FILES:
+
CLIENT_LONG_FLAG:
+
CLIENT_LONG_PASSWORD:
+
CLIENT_MULTI_QUERIES:
+
CLIENT_MULTI_RESULTS:
+
CLIENT_MULTI_STATEMENTS:
+
CLIENT_NET_READ_TIMEOUT:
+
CLIENT_NET_WRITE_TIMEOUT:
+
CLIENT_NO_SCHEMA:
+
CLIENT_ODBC:
+
CLIENT_PROTOCOL_41:
+
CLIENT_REMEMBER_OPTIONS:
+
CLIENT_RESERVED:
+
CLIENT_SECURE_CONNECTION:
+
CLIENT_SSL:
+
CLIENT_TRANSACTIONS:
+
close:
+
CMDSTATUS_LEN:
+
Collate:
+
Collation:
+
collation_needed:
+
collation_needed16:
+
Collation_needed_func:
+
Column:
+
column_blob:
+
columnBuilder:
+
column_bytes:
+
column_bytes16:
+
column_count:
+
column_decltype:
+
column_decltype16:
+
column_double:
+
column_int:
+
column_int64:
+
ColumnMigrationType:
+
column_name:
+
column_name16:
+
columns:
+
column_table_name:
+
column_text:
+
column_text16:
+
column_type:
+
COM_BINLOG_DUMP:
+
COM_CHANGE_USER:
+
COM_CONNECT:
+
COM_CONNECT_OUT:
+
COM_CREATE_DB:
+
COM_DEBUG:
+
COM_DELAYED_INSERT:
+
COM_DROP_DB:
+
COM_END:
+
COM_FIELD_LIST:
+
COM_INIT_DB:
+
commit:
+
commit_hook:
+
COMP_HEADER_SIZE:
+
COM_PING:
+
complete:
+
complete16:
+
COM_PROCESS_INFO:
+
COM_PROCESS_KILL:
+
COM_QUERY:
+
COM_QUIT:
+
COM_REFRESH:
+
COM_REGISTER_SLAVE:
+
COM_SET_OPTION:
+
COM_SHUTDOWN:
+
COM_SLEEP:
+
COM_STATISTICS:
+
COM_STMT_CLOSE:
+
COM_STMT_EXECUTE:
+
COM_STMT_FETCH:
+
COM_STMT_PREPARE:
+
COM_STMT_RESET:
+
COM_STMT_SEND_LONG_DATA:
+
COM_TABLE_DUMP:
+
COM_TIME:
+
Connection:
+
CONNECTION_AUTH_OK:
+
CONNECTION_AWAITING_RESPONSE:
+
CONNECTION_BAD:
+
CONNECTION_CHECK_TARGET:
+
CONNECTION_CHECK_WRITABLE:
+
CONNECTION_CONSUME:
+
CONNECTION_GSS_STARTUP:
+
ConnectionInfo:
+
CONNECTION_MADE:
+
CONNECTION_NEEDED:
+
CONNECTION_OK:
+
Connections:
+
CONNECTION_SETENV:
+
CONNECTION_SSL_STARTUP:
+
CONNECTION_STARTED:
+
ConnStatusType:
+
count:
+
countBuilder:
+
COUNT_ERROR:
+
Create:
+
create:
+
create_collation:
+
create_collation16:
+
Create_collation_func:
+
createColumnString:
+
createForeignKey:
+
createForeignString:
+
create_function:
+
create_function16:
+
Create_function_final_func:
+
Create_function_func_func:
+
Create_function_step_func:
+
createIndexString:
+
createMigrationTable:
+
create_random_string:
+
createSchema:
+
createSequenceTable:
+
CreateTable:
+
createTable:
+
cuint:
+
Current:
+
CurrentOnUpdate:
+
CURSOR_TYPE_FOR_UPDATE:
+
CURSOR_TYPE_NO_CURSOR:
+
CURSOR_TYPE_READ_ONLY:
+
CURSOR_TYPE_SCROLLABLE:
+
DATA:
+
data_count:
+
data_seek:
+
DATA_TRUNCATED:
+
date:
+
datetime:
+
dbArray:
+
dbBindParamError:
+
dbBit:
+
dbBlob:
+
dbBool:
+
dbBox:
+
dbCircle:
+
DbColumn:
+
DbColumns:
+
dbComposite:
+
dbDate:
+
dbDatetime:
+
dbDecimal:
+
DbEffect:
+
dbEnum:
+
DbError:
+
dbError:
+
dbFixedChar:
+
dbFloat:
+
dbFormat:
+
dbGeometry:
+
dbInet:
+
dbInt:
+
dbJson:
+
dbLine:
+
dbLseg:
+
dbMacAddress:
+
dbNull:
+
dbOpen:
+
dbPath:
+
dbPoint:
+
dbPolygon:
+
DbRows:
+
dbSerial:
+
dbSet:
+
dbTime:
+
dbTimeInterval:
+
dbTimestamp:
+
DbType:
+
DbTypeKind:
+
dbUInt:
+
dbUnknown:
+
dbUrl:
+
dbUser1:
+
dbUser2:
+
dbUser3:
+
dbUser4:
+
dbUser5:
+
dbUuid:
+
dbVarchar:
+
dbXml:
+
debug:
+
decimal:
+
DECIMAL_RESULT:
+
default:
+
DefaultDateTime:
+
delete:
+
deleteBuilder:
+
deleteByIdBuilder:
+
deleteByIdSql:
+
deleteSql:
+
Desc:
+
disable_reads_from_master:
+
disable_rpl_parse:
+
double:
+
Drop:
+
drop:
+
DropColumn:
+
dropColumn:
+
DropTable:
+
dropTable:
+
dump_debug_info:
+
DYNAMIC_ARRAY:
+
echoErrorMsg:
+
echoWarningMsg:
+
embedded:
+
enable_reads_from_master:
+
enable_rpl_parse:
+
Enum_cursor_type:
+
enumField:
+
Enum_field_types:
+
ENUM_FLAG:
+
Enum_mysql_set_option:
+
Enum_mysql_stmt_state:
+
Enum_server_command:
+
Enum_shutdown_level:
+
Enum_stmt_attr_type:
+
eof:
+
errcode:
+
errmsg:
+
errmsg16:
+
ERRMSG_SIZE:
+
errno:
+
errno_to_sqlstate:
+
error:
+
errorConnectionNum:
+
ERROR_MSG_LENGTH:
+
escape_string:
+
exec:
+
execGetValue:
+
ExecStatusType:
+
execThenSaveHistory:
+
false:
+
fetch:
+
fetch_field:
+
fetch_field_direct:
+
fetch_fields:
+
fetch_lengths:
+
fetch_next_result_nonblocking:
+
fetch_row:
+
fetch_row_nonblocking:
+
fetchSql:
+
FIELD:
+
field_count:
+
FIELD_OFFSET:
+
field_seek:
+
field_tell:
+
FIELD_TYPE_BIT:
+
FIELD_TYPE_BLOB:
+
FIELD_TYPE_CHAR:
+
FIELD_TYPE_DATE:
+
FIELD_TYPE_DATETIME:
+
FIELD_TYPE_DECIMAL:
+
FIELD_TYPE_DOUBLE:
+
FIELD_TYPE_ENUM:
+
FIELD_TYPE_FLOAT:
+
FIELD_TYPE_GEOMETRY:
+
FIELD_TYPE_INT24:
+
FIELD_TYPE_INTERVAL:
+
FIELD_TYPE_LONG:
+
FIELD_TYPE_LONG_BLOB:
+
FIELD_TYPE_LONGLONG:
+
FIELD_TYPE_MEDIUM_BLOB:
+
FIELD_TYPE_NEWDATE:
+
FIELD_TYPE_NEWDECIMAL:
+
FIELD_TYPE_NULL:
+
FIELD_TYPE_SET:
+
FIELD_TYPE_SHORT:
+
FIELD_TYPE_STRING:
+
FIELD_TYPE_TIME:
+
FIELD_TYPE_TIMESTAMP:
+
FIELD_TYPE_TINY:
+
FIELD_TYPE_TINY_BLOB:
+
FIELD_TYPE_VAR_STRING:
+
FIELD_TYPE_YEAR:
+
finalize:
+
find:
+
findPlain:
+
first:
+
firstPlain:
+
float:
+
foreign:
+
ForeignOnDelete:
+
free:
+
free_result:
+
free_result_nonblocking:
+
free_table:
+
fromArray:
+
fromObj:
+
fromObjArray:
+
fromSql:
+
get:
+
get_auxdata:
+
get_character_set_info:
+
get_client_info:
+
get_client_version:
+
getColumns:
+
getColumnTypes:
+
get_host_info:
+
getOsName:
+
get_parameters:
+
getPlain:
+
get_proto_info:
+
get_salt_from_password:
+
get_salt_from_password_323:
+
get_server_info:
+
get_server_version:
+
get_table:
+
get_tty_password:
+
gptr:
+
groupAllSql:
+
groupBy:
+
groupBySql:
+
GROUP_FLAG:
+
hash_password:
+
having:
+
havingSql:
+
hex_string:
+
HOSTNAME_LENGTH:
+
increments:
+
index:
+
info:
+
init:
+
INIT_COMMAND:
+
insert:
+
insert_id:
+
insertId:
+
insertSql:
+
insertValueBuilder:
+
insertValuesBuilder:
+
insertValueSql:
+
insertValuesSql:
+
InstantRow:
+
instantRows:
+
instantRowsPlain:
+
integer:
+
INTERNAL_NUM_FIELD:
+
interrupt:
+
INT_RESULT:
+
IS_BLOB:
+
ISchema:
+
isExistsIndex:
+
isExistsMariadb:
+
isExistsMysql:
+
isExistsPostgres:
+
isExistsSqlite:
+
isExistsSurrealdb:
+
isExistsUnique:
+
IS_NOT_NULL:
+
IS_NUM:
+
IS_NUM_FIELD:
+
IS_PRI_KEY:
+
Item_result:
+
join:
+
joinSql:
+
json:
+
kill:
+
KILL_CONNECTION:
+
KILL_QUERY:
+
last_insert_rowid:
+
leftJoin:
+
leftJoinSql:
+
len:
+
library_end:
+
library_init:
+
libversion:
+
libversion_number:
+
limit:
+
limitSql:
+
list_dbs:
+
list_fields:
+
list_processes:
+
list_tables:
+
load_defaults:
+
LOCAL_HOST:
+
LOCAL_HOST_NAMEDPIPE:
+
LOCAL_INFILE_ERROR_LEN:
+
lo_close:
+
lo_creat:
+
lo_export:
+
logger:
+
LogSetting:
+
lo_import:
+
lo_lseek:
+
LONG_DATA_HEADER:
+
longText:
+
lo_open:
+
lo_read:
+
lo_tell:
+
lo_unlink:
+
lo_write:
+
make_password_from_salt:
+
make_password_from_salt_323:
+
make_scrambled_password:
+
make_scrambled_password_323:
+
MANAGER:
+
MANAGER_ACCESS:
+
MANAGER_CLIENT_ERR:
+
manager_close:
+
manager_command:
+
manager_connect:
+
manager_fetch_line:
+
MANAGER_INFO:
+
manager_init:
+
MANAGER_INTERNAL_ERR:
+
MANAGER_OK:
+
MariaDB:
+
MariadbConnections:
+
MariadbParams:
+
MariadbParamValue:
+
MariadbQuery:
+
MariadbSchema:
+
master_query:
+
master_send_query:
+
max:
+
MAX_BIGINT_WIDTH:
+
MAX_BLOB_WIDTH:
+
maxBuilder:
+
MAX_CHAR_WIDTH:
+
maxFromSql:
+
MAX_INT_WIDTH:
+
MAX_MEDIUMINT_WIDTH:
+
MAX_MYSQL_MANAGER_ERR:
+
MAX_MYSQL_MANAGER_MSG:
+
MAX_SMALLINT_WIDTH:
+
MAX_TINYINT_WIDTH:
+
mediumInteger:
+
mediumText:
+
MEM_ROOT:
+
METHODS:
+
migrationTable:
+
min:
+
minBuilder:
+
modify_defaults_file:
+
more_results:
+
mprintf:
+
MULTIPLE_KEY_FLAG:
+
my_bool:
+
MY_CHARSET_INFO:
+
my_connect:
+
my_init:
+
my_net_init:
+
my_net_local_init:
+
my_net_read:
+
my_net_write:
+
myodbc_remove_escape:
+
my_rnd:
+
my_socket:
+
MySQL:
+
MysqlConnections:
+
MYSQL_OPT_NONBLOCK:
+
MysqlParams:
+
MysqlParamValue:
+
MysqlQuery:
+
MysqlSchema:
+
my_thread_end:
+
my_thread_init:
+
my_ulonglong:
+
NAMEDPIPE:
+
NAME_LEN:
+
NET:
+
NET_ASYNC_COMPLETE:
+
NET_ASYNC_COMPLETE_NO_MORE_RESULTS:
+
NET_ASYNC_ERROR:
+
NET_ASYNC_NOT_READY:
+
NetAsyncStatus:
+
net_clear:
+
net_end:
+
net_flush:
+
NET_HEADER_SIZE:
+
NET_READ_TIMEOUT:
+
net_realloc:
+
net_real_write:
+
net_safe_read:
+
NET_WAIT_TIMEOUT:
+
net_write_command:
+
NET_WRITE_TIMEOUT:
+
new:
+
newRow:
+
next_result:
+
NO_ACTION:
+
NO_DATA:
+
NO_DEFAULT_VALUE_FLAG:
+
None:
+
notAllowedOption:
+
notAllowedType:
+
NOT_NULL_FLAG:
+
nullable:
+
NULL_LENGTH:
+
Numeric:
+
num_fields:
+
NUM_FLAG:
+
num_rows:
+
numToAlphabet:
+
octet2hex:
+
offset:
+
offsetSql:
+
Oid:
+
onDelete:
+
ONLY_KILL_QUERY:
+
onTable:
+
open:
+
open16:
+
OPT_COMPRESS:
+
OPT_CONNECT_TIMEOUT:
+
OPT_GUESS_CONNECTION:
+
Option:
+
OPTION_MULTI_STATEMENTS_OFF:
+
OPTION_MULTI_STATEMENTS_ON:
+
options:
+
OPT_LOCAL_INFILE:
+
OPT_NAMED_PIPE:
+
OPT_PROTOCOL:
+
OPT_READ_TIMEOUT:
+
OPT_RECONNECT:
+
OPT_USE_EMBEDDED_CONNECTION:
+
OPT_USE_REMOTE_CONNECTION:
+
OPT_USE_RESULT:
+
OPT_WRITE_TIMEOUT:
+
Order:
+
orderBy:
+
orderBySql:
+
orm:
+
orWhere:
+
orWhereSql:
+
packet_error:
+
parallel:
+
parallelSql:
+
PARAMETERS:
+
PART_KEY_FLAG:
+
PBIND:
+
Pcharacter_set:
+
Pcharset_info_st:
+
PConnStatusType:
+
Pcontext:
+
PDATA:
+
PExecStatusType:
+
PFIELD:
+
PFIELD_OFFSET:
+
PGCancel:
+
PGconn:
+
PGlobjfuncs:
+
pgNotify:
+
PGParams:
+
Pgptr:
+
PGresAttDesc:
+
PGresAttValue:
+
PGRES_BAD_RESPONSE:
+
PGRES_COMMAND_OK:
+
PGRES_COPY_BOTH:
+
PGRES_COPY_IN:
+
PGRES_COPY_OUT:
+
PGRES_EMPTY_QUERY:
+
PGRES_FATAL_ERROR:
+
PGRES_NONFATAL_ERROR:
+
PGRES_POLLING_ACTIVE:
+
PGRES_POLLING_FAILED:
+
PGRES_POLLING_OK:
+
PGRES_POLLING_READING:
+
PGRES_POLLING_WRITING:
+
PGRES_SINGLE_TUPLE:
+
PGRES_TUPLES_OK:
+
PGresult:
+
PGTransactionStatusType:
+
PGVerbosity:
+
ping:
+
PItem_result:
+
PMANAGER:
+
PMEM_ROOT:
+
PMETHODS:
+
Pmy_bool:
+
PMY_CHARSET_INFO:
+
Pmy_socket:
+
PMySQL:
+
Pmy_ulonglong:
+
PNET:
+
POid:
+
PostgresConnections:
+
PostgresPollingStatusType:
+
PostgreSQL:
+
PostgresQuery:
+
PostgresSchema:
+
PPARAMETERS:
+
PPByte:
+
PPGcancel:
+
PPGconn:
+
PPGlobjfuncs:
+
PPGNotify:
+
PPGresAttDesc:
+
PPGresAttValue:
+
PPGresult:
+
PPGTransactionStatusType:
+
PPGVerbosity:
+
PPostgresPollingStatusType:
+
PPPGresAttDesc:
+
PPPGresAttValue:
+
PPQArgBlock:
+
Ppqbool:
+
PPQconninfoOption:
+
PPQprintOpt:
+
PPSqlite3:
+
PPSqlite3_Backup:
+
PQArgBlock:
+
pqbackendPID:
+
pqbinaryTuples:
+
pqbool:
+
pqCancel:
+
pqclear:
+
pqclientEncoding:
+
pqcmdStatus:
+
pqcmdTuples:
+
pqconndefaults:
+
pqconnectdb:
+
pqconnectionNeedsPassword:
+
pqconnectionUsedPassword:
+
pqconnectPoll:
+
pqconnectStart:
+
pqconninfoFree:
+
PQconninfoOption:
+
pqconsumeInput:
+
pqdb:
+
pqdisplayTuples:
+
pqendcopy:
+
pqenv2encoding:
+
pqerrorMessage:
+
PQERRORS_DEFAULT:
+
PQERRORS_SQLSTATE:
+
PQERRORS_TERSE:
+
PQERRORS_VERBOSE:
+
pqescapeBytea:
+
pqescapeString:
+
pqexec:
+
pqexecParams:
+
pqexecPrepared:
+
pqfformat:
+
pqfinish:
+
pqflush:
+
pqfmod:
+
pqfn:
+
pqfname:
+
pqfnumber:
+
pqFreeCancel:
+
pqfreemem:
+
pqfsize:
+
pqftable:
+
pqftablecol:
+
pqftype:
+
pqGetCancel:
+
pqgetCopyData:
+
pqgetisnull:
+
pqgetlength:
+
pqgetline:
+
pqgetlineAsync:
+
pqgetResult:
+
pqgetvalue:
+
pqhost:
+
pqinitOpenSSL:
+
pqisBusy:
+
pqisnonblocking:
+
pqmakeEmptyPGresult:
+
pqmblen:
+
pqnfields:
+
PQnoticeProcessor:
+
PQnoticeReceiver:
+
pqnotifies:
+
pqntuples:
+
pqoidStatus:
+
pqoidValue:
+
pqoptions:
+
pqparameterStatus:
+
pqpass:
+
pqport:
+
pqprepare:
+
pqprint:
+
PQprintOpt:
+
pqprintTuples:
+
pqprotocolVersion:
+
pqputCopyData:
+
pqputCopyEnd:
+
pqputline:
+
pqputnbytes:
+
pqrequestCancel:
+
pqreset:
+
pqresetPoll:
+
pqresetStart:
+
pqresStatus:
+
pqresultErrorField:
+
pqresultErrorMessage:
+
pqresultStatus:
+
pqsendPrepare:
+
pqsendQuery:
+
pqsendQueryParams:
+
pqsendQueryPrepared:
+
pqserverVersion:
+
pqsetClientEncoding:
+
pqsetdb:
+
pqsetdbLogin:
+
pqsetErrorVerbosity:
+
pqsetnonblocking:
+
pqsetNoticeProcessor:
+
pqsetNoticeReceiver:
+
pqSetSingleRowMode:
+
pqsocket:
+
pqstatus:
+
pqtrace:
+
pqtransactionStatus:
+
PQTRANS_ACTIVE:
+
PQTRANS_IDLE:
+
PQTRANS_INERROR:
+
PQTRANS_INTRANS:
+
PQTRANS_UNKNOWN:
+
pqtty:
+
pqunescapeBytea:
+
pquntrace:
+
pquser:
+
Prand_struct:
+
prepare:
+
prepare16:
+
preparedExec:
+
preparedQuery:
+
prepare_v2:
+
PRES:
+
PRI_KEY_FLAG:
+
progress_handler:
+
PROTOCOL_DEFAULT:
+
PROTOCOL_MEMORY:
+
PROTOCOL_PIPE:
+
PROTOCOL_SOCKET:
+
PROTOCOL_TCP:
+
Protocol_type:
+
PROW:
+
PROW_OFFSET:
+
PROWS:
+
Psockaddr:
+
PSqlite3:
+
PSqlite3_Backup:
+
Pst_dynamic_array:
+
Pst_mem_root:
+
PSTMT:
+
PStmt:
+
Pst_mysql:
+
Pst_mysql_bind:
+
Pst_mysql_data:
+
Pst_mysql_field:
+
Pst_mysql_manager:
+
Pst_mysql_methods:
+
Pst_mysql_options:
+
Pst_mysql_parameters:
+
Pst_mysql_res:
+
Pst_mysql_rows:
+
Pst_mysql_stmt:
+
Pst_net:
+
Pst_udf_args:
+
Pst_udf_init:
+
Pst_used_mem:
+
PUDF_ARGS:
+
PUDF_INIT:
+
PUSED_MEM:
+
PValue:
+
PValueArg:
+
PVIO:
+
query:
+
queryPlain:
+
questionToDaller:
+
quoteColumn:
+
quoteTable:
+
randominit:
+
Rand_struct:
+
raw:
+
rawExec:
+
rawId:
+
RawMariadbQuery:
+
RawMysqlQuery:
+
RawPostgresQuery:
+
rawQuery:
+
RawSqliteQuery:
+
RawSurrealQuery:
+
rdbBigInteger:
+
rdbBinary:
+
rdbBoolean:
+
rdbChar:
+
rdbDate:
+
rdbDatetime:
+
rdbDecimal:
+
rdbDouble:
+
rdbEnumField:
+
rdbFloat:
+
rdbForeign:
+
rdbIncrements:
+
rdbInteger:
+
rdbJson:
+
rdbLongText:
+
rdbMediumInteger:
+
rdbMediumText:
+
rdbSmallInteger:
+
rdbSoftDelete:
+
rdbStrForeign:
+
rdbString:
+
rdbText:
+
rdbTime:
+
rdbTimestamp:
+
rdbTimestamps:
+
RdbTypeKind:
+
rdbUuid:
+
ReadDbEffect:
+
READ_DEFAULT_FILE:
+
READ_DEFAULT_GROUP:
+
read_query_result:
+
reads_from_master_enabled:
+
real_connect:
+
real_connect_start:
+
real_escape_string:
+
real_query:
+
real_query_nonblocking:
+
REAL_RESULT:
+
reference:
+
refresh:
+
REFRESH_DES_KEY_FILE:
+
REFRESH_FAST:
+
REFRESH_GRANT:
+
REFRESH_HOSTS:
+
REFRESH_LOG:
+
REFRESH_MASTER:
+
REFRESH_QUERY_CACHE:
+
REFRESH_QUERY_CACHE_FREE:
+
REFRESH_READ_LOCK:
+
REFRESH_SLAVE:
+
REFRESH_STATUS:
+
REFRESH_TABLES:
+
REFRESH_THREADS:
+
REFRESH_USER_RESOURCES:
+
reload:
+
RenameColumn:
+
renameColumn:
+
RenameTable:
+
renameTable:
+
renameTo:
+
REPORT_DATA_TRUNCATION:
+
RES:
+
reset:
+
resetMigrationTable:
+
resetSequence:
+
resetTable:
+
RESTRICT:
+
result_blob:
+
result_double:
+
result_error:
+
result_error16:
+
Result_func:
+
result_int:
+
result_int64:
+
result_null:
+
result_text:
+
result_text16:
+
result_text16be:
+
result_text16le:
+
result_value:
+
rollback:
+
ROW:
+
Row:
+
ROW_OFFSET:
+
ROW_RESULT:
+
ROWS:
+
row_seek:
+
row_tell:
+
RPL_ADMIN:
+
RPL_MASTER:
+
rpl_parse_enabled:
+
rpl_probe:
+
rpl_query_type:
+
RPL_SLAVE:
+
Rpl_type:
+
scramble:
+
scramble_323:
+
SCRAMBLED_PASSWORD_CHAR_LENGTH:
+
SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
+
SCRAMBLE_LENGTH:
+
SCRAMBLE_LENGTH_323:
+
SECURE_AUTH:
+
seeder:
+
select:
+
selectAvgBuilder:
+
selectAvgSql:
+
selectBuilder:
+
selectByIdSql:
+
selectCountSql:
+
select_db:
+
selectFindBuilder:
+
selectFirstBuilder:
+
selectFirstSql:
+
selectMaxSql:
+
selectMinSql:
+
selectSql:
+
selectSumBuilder:
+
selectSumSql:
+
send_query:
+
server_end:
+
server_init:
+
SERVER_MORE_RESULTS_EXISTS:
+
SERVER_QUERY_NO_GOOD_INDEX_USED:
+
SERVER_QUERY_NO_INDEX_USED:
+
SERVER_STATUS_AUTOCOMMIT:
+
SERVER_STATUS_CURSOR_EXISTS:
+
SERVER_STATUS_DB_DROPPED:
+
SERVER_STATUS_IN_TRANS:
+
SERVER_STATUS_LAST_ROW_SENT:
+
SERVER_STATUS_MORE_RESULTS:
+
SERVER_STATUS_NO_BACKSLASH_ESCAPES:
+
SERVER_VERSION_LENGTH:
+
SERVICENAME:
+
set_authorizer:
+
set_auxdata:
+
set_character_set:
+
SET_CHARSET_DIR:
+
SET_CHARSET_NAME:
+
SET_CLIENT_IP:
+
setColumnInfo:
+
SET_FLAG:
+
set_local_infile_default:
+
set_master:
+
SET_NULL:
+
setRow:
+
set_server_option:
+
SHARED_MEMORY_BASE_NAME:
+
shouldRun:
+
shutdown:
+
SHUTDOWN_DEFAULT:
+
SHUTDOWN_KILLABLE_CONNECT:
+
SHUTDOWN_KILLABLE_LOCK_TABLE:
+
SHUTDOWN_KILLABLE_TRANS:
+
SHUTDOWN_KILLABLE_UPDATE:
+
SHUTDOWN_WAIT_ALL_BUFFERS:
+
SHUTDOWN_WAIT_CONNECTIONS:
+
SHUTDOWN_WAIT_CRITICAL_BUFFERS:
+
SHUTDOWN_WAIT_TRANSACTIONS:
+
SHUTDOWN_WAIT_UPDATES:
+
slave_query:
+
slave_send_query:
+
smallInteger:
+
smallName:
+
snprintf:
+
Sockaddr:
+
SockAddr:
+
softDelete:
+
SQLite3:
+
sqlite3_sleep:
+
SQLITE_ABORT:
+
SQLITE_ALTER_TABLE:
+
SQLITE_ANY:
+
SQLITE_ATTACH:
+
SQLITE_AUTH:
+
SQLITE_BLOB:
+
SQLITE_BUSY:
+
SQLITE_CANTOPEN:
+
SqliteConnections:
+
SQLITE_CONSTRAINT:
+
SQLITE_COPY:
+
SQLITE_CORRUPT:
+
SQLITE_CREATE_INDEX:
+
SQLITE_CREATE_TABLE:
+
SQLITE_CREATE_TEMP_INDEX:
+
SQLITE_CREATE_TEMP_TABLE:
+
SQLITE_CREATE_TEMP_TRIGGER:
+
SQLITE_CREATE_TEMP_VIEW:
+
SQLITE_CREATE_TRIGGER:
+
SQLITE_CREATE_VIEW:
+
SQLITE_DELETE:
+
SQLITE_DENY:
+
SQLITE_DETACH:
+
SQLITE_DETERMINISTIC:
+
SQLITE_DONE:
+
SQLITE_DROP_INDEX:
+
SQLITE_DROP_TABLE:
+
SQLITE_DROP_TEMP_INDEX:
+
SQLITE_DROP_TEMP_TABLE:
+
SQLITE_DROP_TEMP_TRIGGER:
+
SQLITE_DROP_TEMP_VIEW:
+
SQLITE_DROP_TRIGGER:
+
SQLITE_DROP_VIEW:
+
SQLITE_EMPTY:
+
SQLITE_ERROR:
+
SQLITE_FLOAT:
+
SQLITE_FORMAT:
+
SQLITE_FULL:
+
SQLITE_IGNORE:
+
SQLITE_INSERT:
+
SQLITE_INTEGER:
+
SQLITE_INTERNAL:
+
SQLITE_INTERRUPT:
+
SQLITE_IOERR:
+
SQLITE_LOCKED:
+
SQLITE_MISMATCH:
+
SQLITE_MISUSE:
+
SQLITE_NOLFS:
+
SQLITE_NOMEM:
+
SQLITE_NOTADB:
+
SQLITE_NOTFOUND:
+
SQLITE_NULL:
+
SQLITE_OK:
+
SQLITE_PERM:
+
SQLITE_PRAGMA:
+
SQLITE_PROTOCOL:
+
SqliteQuery:
+
SQLITE_RANGE:
+
SQLITE_READ:
+
SQLITE_READONLY:
+
SQLITE_REINDEX:
+
SQLITE_ROW:
+
SQLITE_SCHEMA:
+
SqliteSchema:
+
SQLITE_SELECT:
+
SQLITE_STATIC:
+
SQLITE_TEXT:
+
SQLITE_TOOBIG:
+
SQLITE_TRANSACTION:
+
SQLITE_TRANSIENT:
+
SQLITE_UPDATE:
+
SQLITE_UTF16:
+
SQLITE_UTF16BE:
+
SQLITE_UTF16LE:
+
SQLITE_UTF8:
+
sqlstate:
+
SQLSTATE_LENGTH:
+
ssl_set:
+
start:
+
startSql:
+
stat:
+
Status:
+
STATUS_GET_RESULT:
+
STATUS_READY:
+
STATUS_USE_RESULT:
+
St_dynamic_array:
+
step:
+
St_mem_root:
+
STMT:
+
stmt_affected_rows:
+
STMT_ATTR_CURSOR_TYPE:
+
stmt_attr_get:
+
STMT_ATTR_PREFETCH_ROWS:
+
stmt_attr_set:
+
STMT_ATTR_UPDATE_MAX_LENGTH:
+
stmt_bind_param:
+
stmt_bind_result:
+
stmt_close:
+
stmt_data_seek:
+
stmt_errno:
+
stmt_error:
+
stmt_execute:
+
STMT_EXECUTE_DONE:
+
stmt_fetch:
+
stmt_fetch_column:
+
STMT_FETCH_DONE:
+
stmt_field_count:
+
stmt_free_result:
+
STMT_HEADER:
+
stmt_init:
+
STMT_INIT_DONE:
+
stmt_insert_id:
+
stmt_num_rows:
+
stmt_param_count:
+
stmt_param_metadata:
+
stmt_prepare:
+
STMT_PREPARE_DONE:
+
stmt_reset:
+
stmt_result_metadata:
+
stmt_row_seek:
+
stmt_row_tell:
+
stmt_send_long_data:
+
stmt_sqlstate:
+
stmt_store_result:
+
St_mysql:
+
St_mysql_bind:
+
St_mysql_data:
+
St_mysql_field:
+
St_mysql_manager:
+
St_mysql_methods:
+
St_mysql_options:
+
St_mysql_parameters:
+
St_mysql_res:
+
St_mysql_rows:
+
St_mysql_stmt:
+
St_net:
+
store_result:
+
store_result_nonblocking:
+
strForeign:
+
string:
+
STRING_RESULT:
+
St_udf_args:
+
St_udf_init:
+
St_used_mem:
+
sum:
+
sumBuilder:
+
SurrealConn:
+
SurrealConnections:
+
SurrealDB:
+
SurrealId:
+
SurrealImpl:
+
SurrealQuery:
+
SurrealSchema:
+
table:
+
Table:
+
table:
+
TableMigrationType:
+
Tbind_destructor_func:
+
text:
+
thread_end:
+
thread_id:
+
thread_init:
+
thread_safe:
+
time:
+
timestamp:
+
TIMESTAMP_FLAG:
+
timestamps:
+
toInterface:
+
toSchema:
+
total_changes:
+
trace:
+
transaction:
+
true:
+
TYPE_BIT:
+
TYPE_BLOB:
+
TYPE_DATE:
+
TYPE_DATETIME:
+
TYPE_DECIMAL:
+
TYPE_DOUBLE:
+
TYPE_ENUM:
+
TYPE_FLOAT:
+
TYPE_GEOMETRY:
+
TYPE_INT24:
+
TYPE_LONG:
+
TYPE_LONG_BLOB:
+
TYPE_LONGLONG:
+
TYPE_MEDIUM_BLOB:
+
TYPE_NEWDATE:
+
TYPE_NEWDECIMAL:
+
TYPE_NULL:
+
TYPE_SET:
+
TYPE_SHORT:
+
TYPE_STRING:
+
TYPE_TIME:
+
TYPE_TIMESTAMP:
+
TYPE_TINY:
+
TYPE_TINY_BLOB:
+
TYPE_VARCHAR:
+
TYPE_VAR_STRING:
+
TYPE_YEAR:
+
U:
+
UDF_ARGS:
+
UDF_INIT:
+
unique:
+
UNIQUE_FLAG:
+
UNIQUE_KEY_FLAG:
+
unsigned:
+
UNSIGNED_FLAG:
+
update:
+
updateBuilder:
+
updateMergeBuilder:
+
updateMergeSql:
+
updateSql:
+
updateValuesSql:
+
UsecaseType:
+
USED_MEM:
+
user_data:
+
use_result:
+
USERNAME_LENGTH:
+
uuid:
+
value_blob:
+
value_bytes:
+
value_bytes16:
+
value_double:
+
value_int:
+
value_int64:
+
value_text:
+
value_text16:
+
value_text16be:
+
value_text16le:
+
value_type:
+
version:
+
warning_count:
+
where:
+
whereBetween:
+
whereBetweenSql:
+
whereBetweenStringSql:
+
whereIn:
+
whereInSql:
+
whereNotBetween:
+
whereNotBetweenSql:
+
whereNotBetweenStringSql:
+
whereNotIn:
+
whereNotInSql:
+
whereNull:
+
whereNullSql:
+
whereSql:
+
WriteDbEffect:
+
ZEROFILL_FLAG:
+
+ +
+
+ + + + + + diff --git a/clients/nim-allographer/docs/utils.html b/clients/nim-allographer/docs/utils.html new file mode 100644 index 0000000..cb30296 --- /dev/null +++ b/clients/nim-allographer/docs/utils.html @@ -0,0 +1,104 @@ + + + + + + + +src/allographer/utils + + + + + + + + + + + + +
+
+

src/allographer/utils

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Procs

+
+
+
+
func getOsName(): string {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + + + + diff --git a/clients/nim-allographer/docs/utils.idx b/clients/nim-allographer/docs/utils.idx new file mode 100644 index 0000000..9f94bb3 --- /dev/null +++ b/clients/nim-allographer/docs/utils.idx @@ -0,0 +1,2 @@ +nimTitle utils utils.html module src/allographer/utils 0 +nim getOsName utils.html#getOsName proc getOsName(): string 3 diff --git a/clients/nim-allographer/docs/v2/connection.html b/clients/nim-allographer/docs/v2/connection.html new file mode 100644 index 0000000..7001612 --- /dev/null +++ b/clients/nim-allographer/docs/v2/connection.html @@ -0,0 +1,94 @@ + + + + + + + +src/allographer/connection + + + + + + + + + + + + + + + + + + + + diff --git a/clients/nim-allographer/docs/v2/connection.idx b/clients/nim-allographer/docs/v2/connection.idx new file mode 100644 index 0000000..34ca3d8 --- /dev/null +++ b/clients/nim-allographer/docs/v2/connection.idx @@ -0,0 +1 @@ +nimTitle connection connection.html module src/allographer/connection 0 diff --git a/clients/nim-allographer/docs/v2/dochack.js b/clients/nim-allographer/docs/v2/dochack.js new file mode 100644 index 0000000..0038977 --- /dev/null +++ b/clients/nim-allographer/docs/v2/dochack.js @@ -0,0 +1,1728 @@ +/* Generated by the Nim Compiler v2.2.8 */ +var framePtr = null; +var excHandler = 0; +var lastJSError = null; +var NTI33554466 = {size: 0,kind: 1,base: null,node: null,finalizer: null}; +var NTI738197518 = {size: 0, kind: 18, base: null, node: null, finalizer: null}; +var NTI33554435 = {size: 0,kind: 31,base: null,node: null,finalizer: null}; +var NTI973078610 = {size: 0,kind: 31,base: null,node: null,finalizer: null}; +var NTI973078617 = {size: 0, kind: 18, base: null, node: null, finalizer: null}; +var NTI134217751 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217741 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217743 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217745 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI33555179 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI33555187 = {size: 0, kind: 22, base: null, node: null, finalizer: null}; +var NTI33554450 = {size: 0,kind: 29,base: null,node: null,finalizer: null}; +var NTI33555186 = {size: 0, kind: 22, base: null, node: null, finalizer: null}; +var NTI33555183 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI33555184 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI134217749 = {size: 0, kind: 17, base: null, node: null, finalizer: null}; +var NTI33554449 = {size: 0,kind: 28,base: null,node: null,finalizer: null}; +var NNI134217749 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217749.node = NNI134217749; +var NNI33555184 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI33555184.node = NNI33555184; +NTI33555186.base = NTI33555183; +NTI33555187.base = NTI33555183; +var NNI33555183 = {kind: 2, len: 5, offset: 0, typ: null, name: null, sons: [{kind: 1, offset: "parent", len: 0, typ: NTI33555186, name: "parent", sons: null}, +{kind: 1, offset: "name", len: 0, typ: NTI33554450, name: "name", sons: null}, +{kind: 1, offset: "message", len: 0, typ: NTI33554449, name: "msg", sons: null}, +{kind: 1, offset: "trace", len: 0, typ: NTI33554449, name: "trace", sons: null}, +{kind: 1, offset: "up", len: 0, typ: NTI33555187, name: "up", sons: null}]}; +NTI33555183.node = NNI33555183; +var NNI33555179 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI33555179.node = NNI33555179; +NTI33555183.base = NTI33555179; +NTI33555184.base = NTI33555183; +NTI134217749.base = NTI33555184; +var NNI134217745 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217745.node = NNI134217745; +NTI134217745.base = NTI33555184; +var NNI134217743 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217743.node = NNI134217743; +var NNI134217741 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217741.node = NNI134217741; +NTI134217741.base = NTI33555184; +NTI134217743.base = NTI134217741; +var NNI134217751 = {kind: 2, len: 0, offset: 0, typ: null, name: null, sons: []}; +NTI134217751.node = NNI134217751; +NTI134217751.base = NTI33555184; +var NNI973078617 = {kind: 2, len: 2, offset: 0, typ: null, name: null, sons: [{kind: 1, offset: "a", len: 0, typ: NTI973078610, name: "a", sons: null}, +{kind: 1, offset: "b", len: 0, typ: NTI33554435, name: "b", sons: null}]}; +NTI973078617.node = NNI973078617; +var NNI738197518 = {kind: 2, len: 2, offset: 0, typ: null, name: null, sons: [{kind: 1, offset: "Field0", len: 0, typ: NTI33554435, name: "Field0", sons: null}, +{kind: 1, offset: "Field1", len: 0, typ: NTI33554466, name: "Field1", sons: null}]}; +NTI738197518.node = NNI738197518; + +function setConstr() { + var result = {}; + for (var i = 0; i < arguments.length; ++i) { + var x = arguments[i]; + if (typeof(x) == "object") { + for (var j = x[0]; j <= x[1]; ++j) { + result[j] = true; + } + } else { + result[x] = true; + } + } + return result; + + + +} +var ConstSet1 = setConstr(17, 16, 4, 18, 27, 19, 23, 22, 21); + +function nimCopy(dest_p0, src_p1, ti_p2) { + var result_33557360 = null; + + switch (ti_p2.kind) { + case 21: + case 22: + case 23: + case 5: + if (!(isFatPointer__system_u2908(ti_p2))) { + result_33557360 = src_p1; + } + else { + result_33557360 = [src_p1[0], src_p1[1]]; + } + + break; + case 19: + if (dest_p0 === null || dest_p0 === undefined) { + dest_p0 = {}; + } + else { + for (var key in dest_p0) { delete dest_p0[key]; } + } + for (var key in src_p1) { dest_p0[key] = src_p1[key]; } + result_33557360 = dest_p0; + + break; + case 18: + case 17: + if (!((ti_p2.base == null))) { + result_33557360 = nimCopy(dest_p0, src_p1, ti_p2.base); + } + else { + if ((ti_p2.kind == 17)) { + result_33557360 = (dest_p0 === null || dest_p0 === undefined) ? {m_type: ti_p2} : dest_p0; + } + else { + result_33557360 = (dest_p0 === null || dest_p0 === undefined) ? {} : dest_p0; + } + } + nimCopyAux(result_33557360, src_p1, ti_p2.node); + break; + case 4: + case 16: + if(ArrayBuffer.isView(src_p1)) { + if(dest_p0 === null || dest_p0 === undefined || dest_p0.length != src_p1.length) { + dest_p0 = new src_p1.constructor(src_p1); + } else { + dest_p0.set(src_p1, 0); + } + result_33557360 = dest_p0; + } else { + if (src_p1 === null) { + result_33557360 = null; + } + else { + if (dest_p0 === null || dest_p0 === undefined || dest_p0.length != src_p1.length) { + dest_p0 = new Array(src_p1.length); + } + result_33557360 = dest_p0; + for (var i = 0; i < src_p1.length; ++i) { + result_33557360[i] = nimCopy(result_33557360[i], src_p1[i], ti_p2.base); + } + } + } + + break; + case 24: + case 27: + if (src_p1 === null) { + result_33557360 = null; + } + else { + if (dest_p0 === null || dest_p0 === undefined || dest_p0.length != src_p1.length) { + dest_p0 = new Array(src_p1.length); + } + result_33557360 = dest_p0; + for (var i = 0; i < src_p1.length; ++i) { + result_33557360[i] = nimCopy(result_33557360[i], src_p1[i], ti_p2.base); + } + } + + break; + case 28: + if (src_p1 !== null) { + result_33557360 = src_p1.slice(0); + } + + break; + default: + result_33557360 = src_p1; + break; + } + + return result_33557360; + +} + +function mnewString(len_p0) { + var result = new Array(len_p0); + for (var i = 0; i < len_p0; i++) {result[i] = 0;} + return result; + + + +} + +function nimAddStrStr(x_p0, y_p1) { + var L = y_p1.length; + for (var i = 0; i < L; ++i) { + x_p0.push(y_p1[i]); + } + + + +} + +function isObj(obj_p0, subclass_p1) { + var result_33557473 = false; + + BeforeRet: { + var x_33557474 = obj_p0; + if ((x_33557474 == subclass_p1)) { + result_33557473 = true; + break BeforeRet; + } + + Label1: { + Label2: while (true) { + if (!!((x_33557474 == subclass_p1))) break Label2; + if ((x_33557474 == null)) { + result_33557473 = false; + break BeforeRet; + } + + x_33557474 = x_33557474.base; + } + }; + result_33557473 = true; + break BeforeRet; + }; + + return result_33557473; + +} + +function toJSStr(s_p0) { + var result_33556949 = null; + + var res_33557003 = newSeq__system_u2546((s_p0).length); + var i_33557004 = 0; + var j_33557005 = 0; + Label1: { + Label2: while (true) { + if (!(i_33557004 < (s_p0).length)) break Label2; + var c_33557006 = s_p0[i_33557004]; + if ((c_33557006 < 128)) { + res_33557003[j_33557005] = String.fromCharCode(c_33557006); + i_33557004 += 1; + } + else { + var helper_33557032 = newSeq__system_u2546(0); + Label3: { + Label4: while (true) { + if (!true) break Label4; + var code_33557033 = c_33557006.toString(16); + if ((((code_33557033) == null ? 0 : (code_33557033).length) == 1)) { + helper_33557032.push("%0");; + } + else { + helper_33557032.push("%");; + } + + helper_33557032.push(code_33557033);; + i_33557004 += 1; + if ((((s_p0).length <= i_33557004) || (s_p0[i_33557004] < 128))) { + break Label3; + } + + c_33557006 = s_p0[i_33557004]; + } + }; +++excHandler; + try { + res_33557003[j_33557005] = decodeURIComponent(helper_33557032.join("")); +--excHandler; +} catch (EXCEPTION) { + var prevJSError = lastJSError; + lastJSError = EXCEPTION; + --excHandler; + raiseDefect(); + res_33557003[j_33557005] = helper_33557032.join(""); + lastJSError = prevJSError; + } finally { + } + } + + j_33557005 += 1; + } + }; + if (res_33557003.length < j_33557005) { for (var i = res_33557003.length ; i < j_33557005 ; ++i) res_33557003.push(null); } + else { res_33557003.length = j_33557005; }; + result_33556949 = res_33557003.join(""); + + return result_33556949; + +} + +function raiseException(e_p0, ename_p1) { + e_p0.name = ename_p1; + if ((excHandler == 0)) { + unhandledException(e_p0); + } + + throw e_p0; + + +} + +function chckIndx(i_p0, a_p1, b_p2) { + var result_33557395 = 0; + + BeforeRet: { + if (((a_p1 <= i_p0) && (i_p0 <= b_p2))) { + result_33557395 = i_p0; + break BeforeRet; + } + else { + raiseIndexError(i_p0, a_p1, b_p2); + } + + }; + + return result_33557395; + +} + +function makeNimstrLit(c_p0) { + var result = []; + for (var i = 0; i < c_p0.length; ++i) { + result[i] = c_p0.charCodeAt(i); + } + return result; + + + +} + +function addInt(a_p0, b_p1) { + var result = a_p0 + b_p1; + checkOverflowInt(result); + return result; + + + +} + +function subInt(a_p0, b_p1) { + var result = a_p0 - b_p1; + checkOverflowInt(result); + return result; + + + +} + +function chckRange(i_p0, a_p1, b_p2) { + var result_33557400 = 0; + + BeforeRet: { + if (((a_p1 <= i_p0) && (i_p0 <= b_p2))) { + result_33557400 = i_p0; + break BeforeRet; + } + else { + raiseRangeError(); + } + + }; + + return result_33557400; + +} + +function cstrToNimstr(c_p0) { + var ln = c_p0.length; + var result = new Array(ln); + var r = 0; + for (var i = 0; i < ln; ++i) { + var ch = c_p0.charCodeAt(i); + + if (ch < 128) { + result[r] = ch; + } + else { + if (ch < 2048) { + result[r] = (ch >> 6) | 192; + } + else { + if (ch < 55296 || ch >= 57344) { + result[r] = (ch >> 12) | 224; + } + else { + ++i; + ch = 65536 + (((ch & 1023) << 10) | (c_p0.charCodeAt(i) & 1023)); + result[r] = (ch >> 18) | 240; + ++r; + result[r] = ((ch >> 12) & 63) | 128; + } + ++r; + result[r] = ((ch >> 6) & 63) | 128; + } + ++r; + result[r] = (ch & 63) | 128; + } + ++r; + } + return result; + + + +} +var ConstSet2 = setConstr([65, 90]); +var ConstSet3 = setConstr(95, 32, 46); +var ConstSet4 = setConstr(95, 32, 46); + +function mulInt(a_p0, b_p1) { + var result = a_p0 * b_p1; + checkOverflowInt(result); + return result; + + + +} +var ConstSet5 = setConstr([97, 122]); +var ConstSet6 = setConstr([65, 90], [97, 122]); +var ConstSet7 = setConstr([97, 122]); +var ConstSet8 = setConstr([65, 90]); +var ConstSet9 = setConstr([65, 90], [97, 122]); + +function nimMax(a_p0, b_p1) { + var Temporary1; + + var result_33557187 = 0; + + BeforeRet: { + if ((b_p1 <= a_p0)) { + Temporary1 = a_p0; + } + else { + Temporary1 = b_p1; + } + + result_33557187 = Temporary1; + break BeforeRet; + }; + + return result_33557187; + +} + +function nimMin(a_p0, b_p1) { + var Temporary1; + + var result_33557183 = 0; + + BeforeRet: { + if ((a_p0 <= b_p1)) { + Temporary1 = a_p0; + } + else { + Temporary1 = b_p1; + } + + result_33557183 = Temporary1; + break BeforeRet; + }; + + return result_33557183; + +} + +function addChar(x_p0, c_p1) { + x_p0.push(c_p1); + + +} +var objectID_1191182514 = [0]; + +function setTheme(theme_p0) { + document.documentElement.setAttribute("data-theme", theme_p0); + window.localStorage.setItem("theme", theme_p0); + + +} + +function isFatPointer__system_u2908(ti_p0) { + var result_33557342 = false; + + BeforeRet: { + result_33557342 = !((ConstSet1[ti_p0.base.kind] != undefined)); + break BeforeRet; + }; + + return result_33557342; + +} + +function nimCopyAux(dest_p0, src_p1, n_p2) { + switch (n_p2.kind) { + case 0: + break; + case 1: + dest_p0[n_p2.offset] = nimCopy(dest_p0[n_p2.offset], src_p1[n_p2.offset], n_p2.typ); + + break; + case 2: + for (var i = 0; i < n_p2.sons.length; i++) { + nimCopyAux(dest_p0, src_p1, n_p2.sons[i]); + } + + break; + case 3: + dest_p0[n_p2.offset] = nimCopy(dest_p0[n_p2.offset], src_p1[n_p2.offset], n_p2.typ); + for (var i = 0; i < n_p2.sons.length; ++i) { + nimCopyAux(dest_p0, src_p1, n_p2.sons[i][1]); + } + + break; + } + + +} + +function addChars__stdZprivateZdigitsutils_u202(result_p0, result_p0_Idx, x_p1, start_p2, n_p3) { + var Temporary1; + + var old_335544528 = (result_p0[result_p0_Idx]).length; + if (result_p0[result_p0_Idx].length < (Temporary1 = (old_335544528 + n_p3), Temporary1)) { for (var i = result_p0[result_p0_Idx].length; i < Temporary1; ++i) result_p0[result_p0_Idx].push(0); } + else {result_p0[result_p0_Idx].length = Temporary1; }; + Label2: { + var iHEX60gensym4_335544542 = 0; + var i_587203799 = 0; + Label3: { + Label4: while (true) { + if (!(i_587203799 < n_p3)) break Label4; + iHEX60gensym4_335544542 = i_587203799; + result_p0[result_p0_Idx][(old_335544528 + iHEX60gensym4_335544542)] = x_p1.charCodeAt((start_p2 + iHEX60gensym4_335544542)); + i_587203799 += 1; + } + }; + }; + + +} + +function addChars__stdZprivateZdigitsutils_u198(result_p0, result_p0_Idx, x_p1) { + addChars__stdZprivateZdigitsutils_u202(result_p0, result_p0_Idx, x_p1, 0, ((x_p1) == null ? 0 : (x_p1).length)); + + +} + +function addInt__stdZprivateZdigitsutils_u223(result_p0, result_p0_Idx, x_p1) { + addChars__stdZprivateZdigitsutils_u198(result_p0, result_p0_Idx, ((x_p1) + "")); + + +} + +function addInt__stdZprivateZdigitsutils_u241(result_p0, result_p0_Idx, x_p1) { + addInt__stdZprivateZdigitsutils_u223(result_p0, result_p0_Idx, BigInt(x_p1)); + + +} + +function HEX24__systemZdollars_u14(xHEX60gensym0_p0) { + var result_402653200 = [[]]; + + result_402653200[0] = nimCopy(null, [], NTI33554449); + addInt__stdZprivateZdigitsutils_u241(result_402653200, 0, xHEX60gensym0_p0); + + return result_402653200[0]; + +} + +function add__system_u1972(x_p0, x_p0_Idx, y_p1) { + if (x_p0[x_p0_Idx] === null) { x_p0[x_p0_Idx] = []; } + var off = x_p0[x_p0_Idx].length; + x_p0[x_p0_Idx].length += y_p1.length; + for (var i = 0; i < y_p1.length; ++i) { + x_p0[x_p0_Idx][off+i] = y_p1.charCodeAt(i); + } + + + +} + +function newSeq__system_u2546(len_p0) { + var result_33556982 = []; + + result_33556982 = new Array(len_p0); for (var i = 0 ; i < len_p0 ; ++i) { result_33556982[i] = null; } + return result_33556982; + +} + +function isNimException__system_u2043() { + return lastJSError && lastJSError.m_type; + + +} + +function getCurrentException() { + var result_33556478 = null; + + if (isNimException__system_u2043()) { + result_33556478 = lastJSError; + } + + + return result_33556478; + +} + +function raiseDefect() { + if (isNimException__system_u2043()) { + var e_33556694 = getCurrentException(); + if (isObj(e_33556694.m_type, NTI33555184)) { + if ((excHandler == 0)) { + unhandledException(e_33556694); + } + + throw e_33556694; + } + + } + + + +} + +function unhandledException(e_p0) { + var buf_33556688 = [[]]; + if (!(((e_p0.message).length == 0))) { + nimAddStrStr(buf_33556688[0], [69,114,114,111,114,58,32,117,110,104,97,110,100,108,101,100,32,101,120,99,101,112,116,105,111,110,58,32]);; + nimAddStrStr(buf_33556688[0], e_p0.message);; + } + else { + nimAddStrStr(buf_33556688[0], [69,114,114,111,114,58,32,117,110,104,97,110,100,108,101,100,32,101,120,99,101,112,116,105,111,110]);; + } + + nimAddStrStr(buf_33556688[0], [32,91]);; + add__system_u1972(buf_33556688, 0, e_p0.name); + nimAddStrStr(buf_33556688[0], [93,10]);; + var cbuf_33556689 = toJSStr(buf_33556688[0]); + if (typeof(Error) !== "undefined") { + throw new Error(cbuf_33556689); + } + else { + throw cbuf_33556689; + } + + + +} + +function raiseIndexError(i_p0, a_p1, b_p2) { + var Temporary1; + + if ((b_p2 < a_p1)) { + Temporary1 = [105,110,100,101,120,32,111,117,116,32,111,102,32,98,111,117,110,100,115,44,32,116,104,101,32,99,111,110,116,97,105,110,101,114,32,105,115,32,101,109,112,116,121]; + } + else { + Temporary1 = ([105,110,100,101,120,32]).concat(HEX24__systemZdollars_u14(i_p0),[32,110,111,116,32,105,110,32],HEX24__systemZdollars_u14(a_p1),[32,46,46,32],HEX24__systemZdollars_u14(b_p2)); + } + + raiseException({message: nimCopy(null, Temporary1, NTI33554449), parent: null, m_type: NTI134217749, name: null, trace: [], up: null}, "IndexDefect"); + + +} + +function sysFatal__stdZassertions_u46(message_p1) { + raiseException({message: nimCopy(null, message_p1, NTI33554449), m_type: NTI134217745, parent: null, name: null, trace: [], up: null}, "AssertionDefect"); + + +} + +function raiseAssert__stdZassertions_u44(msg_p0) { + sysFatal__stdZassertions_u46(msg_p0); + + +} + +function failedAssertImpl__stdZassertions_u86(msg_p0) { + raiseAssert__stdZassertions_u44(msg_p0); + + +} + +function onDOMLoaded(e_p0) { + var Temporary4; + +function HEX3Aanonymous__dochack_u65(event_p0) { + event_p0.target.parentNode.style.display = "none"; + event_p0.target.parentNode.nextSibling.style.display = "inline"; + + +} + + document.getElementById("theme-select").value = window.localStorage.getItem("theme"); + Label1: { + var pragmaDots_587202624 = null; + var colontmp__587203790 = []; + colontmp__587203790 = document.getElementsByClassName("pragmadots"); + var i_587203792 = 0; + var L_587203793 = (colontmp__587203790).length; + Label2: { + Label3: while (true) { + if (!(i_587203792 < L_587203793)) break Label3; + pragmaDots_587202624 = colontmp__587203790[chckIndx(i_587203792, 0, (colontmp__587203790).length - 1)]; + Temporary4 = HEX3Aanonymous__dochack_u65.bind(null); Temporary4.ClP_0 = HEX3Aanonymous__dochack_u65; Temporary4.ClE_0 = null; + pragmaDots_587202624.onclick = Temporary4; + i_587203792 += 1; + if (!(((colontmp__587203790).length == L_587203793))) { + failedAssertImpl__stdZassertions_u86(makeNimstrLit("iterators.nim(254, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + + +} + +function raiseOverflow() { + raiseException({message: [111,118,101,114,45,32,111,114,32,117,110,100,101,114,102,108,111,119], parent: null, m_type: NTI134217743, name: null, trace: [], up: null}, "OverflowDefect"); + + +} + +function checkOverflowInt(a_p0) { + if (a_p0 > 2147483647 || a_p0 < -2147483648) raiseOverflow(); + + + +} + +function isWhitespace__dochack_u408(x_p0) { + var result_587202970 = false; + + result_587202970 = (((x_p0.nodeName == "#text") && !/\S/.test(x_p0.textContent)) || (x_p0.nodeName == "#comment")); + + return result_587202970; + +} + +function toToc__dochack_u411(x_p0, father_p1) { + var Temporary5; + var Temporary6; + var Temporary7; + var Temporary8; + var Temporary15; + + if ((x_p0.nodeName == "UL")) { + var f_587202981 = {heading: null, kids: [], sortId: (father_p1.kids).length, doSort: false}; + var i_587202982 = 0; + Label1: { + Label2: while (true) { + if (!(i_587202982 < x_p0.childNodes.length)) break Label2; + var nxt_587202983 = addInt(i_587202982, 1); + Label3: { + Label4: while (true) { + if (!(nxt_587202983 < x_p0.childNodes.length)) Temporary5 = false; else { Temporary5 = isWhitespace__dochack_u408(x_p0.childNodes[nxt_587202983]); } if (!Temporary5) break Label4; + nxt_587202983 = addInt(nxt_587202983, 1); + } + }; + if (!(nxt_587202983 < x_p0.childNodes.length)) Temporary8 = false; else { Temporary8 = (x_p0.childNodes[i_587202982].nodeName == "LI"); } if (!Temporary8) Temporary7 = false; else { Temporary7 = (x_p0.childNodes[i_587202982].childNodes.length == 1); } if (!Temporary7) Temporary6 = false; else { Temporary6 = (x_p0.childNodes[nxt_587202983].nodeName == "UL"); } if (Temporary6) { + var e_587202996 = {heading: x_p0.childNodes[i_587202982].childNodes[0], kids: [], sortId: (f_587202981.kids).length, doSort: false}; + var it_587202997 = x_p0.childNodes[nxt_587202983]; + Label9: { + var j_587203002 = 0; + var colontmp__587203807 = 0; + colontmp__587203807 = it_587202997.childNodes.length; + var i_587203808 = 0; + Label10: { + Label11: while (true) { + if (!(i_587203808 < colontmp__587203807)) break Label11; + j_587203002 = i_587203808; + toToc__dochack_u411(it_587202997.childNodes[j_587203002], e_587202996); + i_587203808 = addInt(i_587203808, 1); + } + }; + }; + f_587202981.kids.push(e_587202996);; + i_587202982 = addInt(nxt_587202983, 1); + } + else { + toToc__dochack_u411(x_p0.childNodes[i_587202982], f_587202981); + i_587202982 = addInt(i_587202982, 1); + } + + } + }; + father_p1.kids.push(f_587202981);; + } + else { + if (isWhitespace__dochack_u408(x_p0)) { + } + else { + if ((x_p0.nodeName == "LI")) { + var idx_587203020 = []; + Label12: { + var i_587203025 = 0; + var colontmp__587203811 = 0; + colontmp__587203811 = x_p0.childNodes.length; + var i_587203812 = 0; + Label13: { + Label14: while (true) { + if (!(i_587203812 < colontmp__587203811)) break Label14; + i_587203025 = i_587203812; + if (!(isWhitespace__dochack_u408(x_p0.childNodes[i_587203025]))) { + idx_587203020.push(i_587203025);; + } + + i_587203812 = addInt(i_587203812, 1); + } + }; + }; + if (!((idx_587203020).length == 2)) Temporary15 = false; else { Temporary15 = (x_p0.childNodes[idx_587203020[chckIndx(1, 0, (idx_587203020).length - 1)]].nodeName == "UL"); } if (Temporary15) { + var e_587203041 = {heading: x_p0.childNodes[idx_587203020[chckIndx(0, 0, (idx_587203020).length - 1)]], kids: [], sortId: (father_p1.kids).length, doSort: false}; + var it_587203042 = x_p0.childNodes[idx_587203020[chckIndx(1, 0, (idx_587203020).length - 1)]]; + Label16: { + var j_587203047 = 0; + var colontmp__587203815 = 0; + colontmp__587203815 = it_587203042.childNodes.length; + var i_587203816 = 0; + Label17: { + Label18: while (true) { + if (!(i_587203816 < colontmp__587203815)) break Label18; + j_587203047 = i_587203816; + toToc__dochack_u411(it_587203042.childNodes[j_587203047], e_587203041); + i_587203816 = addInt(i_587203816, 1); + } + }; + }; + father_p1.kids.push(e_587203041);; + } + else { + Label19: { + var i_587203056 = 0; + var colontmp__587203819 = 0; + colontmp__587203819 = x_p0.childNodes.length; + var i_587203820 = 0; + Label20: { + Label21: while (true) { + if (!(i_587203820 < colontmp__587203819)) break Label21; + i_587203056 = i_587203820; + toToc__dochack_u411(x_p0.childNodes[i_587203056], father_p1); + i_587203820 = addInt(i_587203820, 1); + } + }; + }; + } + + } + else { + father_p1.kids.push({heading: x_p0, kids: [], sortId: (father_p1.kids).length, doSort: false});; + } + }} + + +} + +function extractItems__dochack_u199(x_p0, heading_p1, items_p2, items_p2_Idx) { + BeforeRet: { + if ((x_p0 == null)) { + break BeforeRet; + } + + if ((!((x_p0.heading == null)) && (x_p0.heading.textContent == heading_p1))) { + Label1: { + var i_587202779 = 0; + var colontmp__587203823 = 0; + colontmp__587203823 = (x_p0.kids).length; + var i_587203824 = 0; + Label2: { + Label3: while (true) { + if (!(i_587203824 < colontmp__587203823)) break Label3; + i_587202779 = i_587203824; + items_p2[items_p2_Idx].push(x_p0.kids[chckIndx(i_587202779, 0, (x_p0.kids).length - 1)].heading);; + i_587203824 = addInt(i_587203824, 1); + } + }; + }; + } + else { + Label4: { + var k_587202805 = null; + var i_587203828 = 0; + var L_587203829 = (x_p0.kids).length; + Label5: { + Label6: while (true) { + if (!(i_587203828 < L_587203829)) break Label6; + k_587202805 = x_p0.kids[chckIndx(i_587203828, 0, (x_p0.kids).length - 1)]; + extractItems__dochack_u199(k_587202805, heading_p1, items_p2, items_p2_Idx); + i_587203828 += 1; + if (!(((x_p0.kids).length == L_587203829))) { + failedAssertImpl__stdZassertions_u86(makeNimstrLit("iterators.nim(254, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + } + + }; + + +} + +function tree__dochack_u130(tag_p0, kids_p1) { + var result_587202693 = null; + + result_587202693 = document.createElement(tag_p0); + Label1: { + var k_587202707 = null; + var i_587203841 = 0; + Label2: { + Label3: while (true) { + if (!(i_587203841 < (kids_p1).length)) break Label3; + k_587202707 = kids_p1[chckIndx(i_587203841, 0, (kids_p1).length - 1)]; + result_587202693.appendChild(k_587202707); + i_587203841 += 1; + } + }; + }; + + return result_587202693; + +} + +function text__dochack_u155(s_p0) { + var result_587202717 = null; + + result_587202717 = document.createTextNode(s_p0); + + return result_587202717; + +} + +function uncovered__dochack_u600(x_p0) { + var Temporary1; + + var result_587203162 = null; + + BeforeRet: { + if ((((x_p0.kids).length == 0) && !((x_p0.heading == null)))) { + if (!(x_p0.heading.hasOwnProperty('__karaxMarker__'))) { + Temporary1 = x_p0; + } + else { + Temporary1 = null; + } + + result_587203162 = Temporary1; + break BeforeRet; + } + + result_587203162 = {heading: x_p0.heading, kids: [], sortId: x_p0.sortId, doSort: x_p0.doSort}; + Label2: { + var k_587203177 = null; + var i_587203848 = 0; + var L_587203849 = (x_p0.kids).length; + Label3: { + Label4: while (true) { + if (!(i_587203848 < L_587203849)) break Label4; + k_587203177 = x_p0.kids[chckIndx(i_587203848, 0, (x_p0.kids).length - 1)]; + var y_587203178 = uncovered__dochack_u600(k_587203177); + if (!((y_587203178 == null))) { + result_587203162.kids.push(y_587203178);; + } + + i_587203848 += 1; + if (!(((x_p0.kids).length == L_587203849))) { + failedAssertImpl__stdZassertions_u86(makeNimstrLit("iterators.nim(254, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + if (((result_587203162.kids).length == 0)) { + result_587203162 = null; + } + + }; + + return result_587203162; + +} + +function mergeTocs__dochack_u630(orig_p0, news_p1) { + var result_587203193 = null; + + result_587203193 = uncovered__dochack_u600(orig_p0); + if ((result_587203193 == null)) { + result_587203193 = news_p1; + } + else { + Label1: { + var i_587203205 = 0; + var colontmp__587203844 = 0; + colontmp__587203844 = (news_p1.kids).length; + var i_587203845 = 0; + Label2: { + Label3: while (true) { + if (!(i_587203845 < colontmp__587203844)) break Label3; + i_587203205 = i_587203845; + result_587203193.kids.push(news_p1.kids[chckIndx(i_587203205, 0, (news_p1.kids).length - 1)]);; + i_587203845 = addInt(i_587203845, 1); + } + }; + }; + } + + + return result_587203193; + +} + +function buildToc__dochack_u650(orig_p0, types_p1, procs_p2) { + var result_587203214 = null; + + var newStuff_587203219 = {heading: null, kids: [], doSort: true, sortId: 0}; + Label1: { + var t_587203223 = null; + var i_587203836 = 0; + var L_587203837 = (types_p1).length; + Label2: { + Label3: while (true) { + if (!(i_587203836 < L_587203837)) break Label3; + t_587203223 = types_p1[chckIndx(i_587203836, 0, (types_p1).length - 1)]; + var c_587203228 = {heading: t_587203223.cloneNode(true), kids: [], doSort: true, sortId: 0}; + t_587203223.__karaxMarker__ = true; + Label4: { + var p_587203232 = null; + var i_587203833 = 0; + var L_587203834 = (procs_p2).length; + Label5: { + Label6: while (true) { + if (!(i_587203833 < L_587203834)) break Label6; + p_587203232 = procs_p2[chckIndx(i_587203833, 0, (procs_p2).length - 1)]; + if (!(p_587203232.hasOwnProperty('__karaxMarker__'))) { + var xx_587203233 = p_587203232.parentNode.getElementsByClassName("attachedType"); + if ((((xx_587203233).length == 1) && (xx_587203233[chckIndx(0, 0, (xx_587203233).length - 1)].textContent == t_587203223.textContent))) { + var q_587203238 = tree__dochack_u130("A", [text__dochack_u155(p_587203232.title)]); + q_587203238.setAttribute("href", p_587203232.getAttribute("href")); + c_587203228.kids.push({heading: q_587203238, kids: [], sortId: 0, doSort: false});; + p_587203232.__karaxMarker__ = true; + } + + } + + i_587203833 += 1; + if (!(((procs_p2).length == L_587203834))) { + failedAssertImpl__stdZassertions_u86(makeNimstrLit("iterators.nim(254, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + newStuff_587203219.kids.push(c_587203228);; + i_587203836 += 1; + if (!(((types_p1).length == L_587203837))) { + failedAssertImpl__stdZassertions_u86(makeNimstrLit("iterators.nim(254, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + result_587203214 = mergeTocs__dochack_u630(orig_p0, newStuff_587203219); + + return result_587203214; + +} + +function add__dochack_u148(parent_p0, kid_p1) { + if (((parent_p0.nodeName == "TR") && ((kid_p1.nodeName == "TD") || (kid_p1.nodeName == "TH")))) { + var k_587202711 = document.createElement("TD"); + k_587202711.appendChild(kid_p1); + parent_p0.appendChild(k_587202711); + } + else { + parent_p0.appendChild(kid_p1); + } + + + +} + +function setClass__dochack_u152(e_p0, value_p1) { + e_p0.setAttribute("class", value_p1); + + +} + +function toHtml__dochack_u278(x_p0, isRoot_p1) { + var Temporary1; + +function HEX3Aanonymous__dochack_u298(a_p0, b_p1) { + var result_587202861 = 0; + + BeforeRet: { + if ((!((a_p0.heading == null)) && !((b_p1.heading == null)))) { + var x_587202870 = a_p0.heading.textContent; + var y_587202871 = b_p1.heading.textContent; + if ((x_587202870 < y_587202871)) { + result_587202861 = (-1); + break BeforeRet; + } + + if ((y_587202871 < x_587202870)) { + result_587202861 = 1; + break BeforeRet; + } + + result_587202861 = 0; + break BeforeRet; + } + else { + result_587202861 = subInt(a_p0.sortId, b_p1.sortId); + break BeforeRet; + } + + }; + + return result_587202861; + +} + + var result_587202841 = null; + + BeforeRet: { + if ((x_p0 == null)) { + result_587202841 = null; + break BeforeRet; + } + + if (((x_p0.kids).length == 0)) { + if ((x_p0.heading == null)) { + result_587202841 = null; + break BeforeRet; + } + + result_587202841 = x_p0.heading.cloneNode(true); + break BeforeRet; + } + + result_587202841 = tree__dochack_u130("DIV", []); + if ((!((x_p0.heading == null)) && !(x_p0.heading.hasOwnProperty('__karaxMarker__')))) { + add__dochack_u148(result_587202841, x_p0.heading.cloneNode(true)); + } + + var ul_587202857 = tree__dochack_u130("UL", []); + if (isRoot_p1) { + setClass__dochack_u152(ul_587202857, "simple simple-toc"); + } + else { + setClass__dochack_u152(ul_587202857, "simple"); + } + + if (x_p0.doSort) { + Temporary1 = HEX3Aanonymous__dochack_u298.bind(null); Temporary1.ClP_0 = HEX3Aanonymous__dochack_u298; Temporary1.ClE_0 = null; + x_p0.kids.sort(Temporary1); + } + + Label2: { + var k_587202883 = null; + var i_587203852 = 0; + var L_587203853 = (x_p0.kids).length; + Label3: { + Label4: while (true) { + if (!(i_587203852 < L_587203853)) break Label4; + k_587202883 = x_p0.kids[chckIndx(i_587203852, 0, (x_p0.kids).length - 1)]; + var y_587202884 = toHtml__dochack_u278(k_587202883, false); + if (!((y_587202884 == null))) { + add__dochack_u148(ul_587202857, tree__dochack_u130("LI", [y_587202884])); + } + + i_587203852 += 1; + if (!(((x_p0.kids).length == L_587203853))) { + failedAssertImpl__stdZassertions_u86(makeNimstrLit("iterators.nim(254, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + if (!((ul_587202857.childNodes.length == 0))) { + add__dochack_u148(result_587202841, ul_587202857); + } + + if ((result_587202841.childNodes.length == 0)) { + result_587202841 = null; + } + + }; + + return result_587202841; + +} + +function replaceById__dochack_u158(id_p0, newTree_p1) { + var x_587202721 = document.getElementById(id_p0); + x_587202721.parentNode.replaceChild(newTree_p1, x_587202721); + newTree_p1.id = id_p0; + + +} + +function togglevis__dochack_u708(d_p0) { + if ((d_p0.style.display == "none")) { + d_p0.style.display = "inline"; + } + else { + d_p0.style.display = "none"; + } + + + +} + +function groupBy(value_p0) { + var toc_587203272 = document.getElementById("toc-list"); + if ((alternative_587203267[0] == null)) { + var tt_587203280 = {heading: null, kids: [], sortId: 0, doSort: false}; + toToc__dochack_u411(toc_587203272, tt_587203280); + tt_587203280 = tt_587203280.kids[chckIndx(0, 0, (tt_587203280.kids).length - 1)]; + var types_587203285 = [[]]; + var procs_587203290 = [[]]; + extractItems__dochack_u199(tt_587203280, "Types", types_587203285, 0); + extractItems__dochack_u199(tt_587203280, "Procs", procs_587203290, 0); + extractItems__dochack_u199(tt_587203280, "Converters", procs_587203290, 0); + extractItems__dochack_u199(tt_587203280, "Methods", procs_587203290, 0); + extractItems__dochack_u199(tt_587203280, "Templates", procs_587203290, 0); + extractItems__dochack_u199(tt_587203280, "Macros", procs_587203290, 0); + extractItems__dochack_u199(tt_587203280, "Iterators", procs_587203290, 0); + var ntoc_587203291 = buildToc__dochack_u650(tt_587203280, types_587203285[0], procs_587203290[0]); + var x_587203292 = toHtml__dochack_u278(ntoc_587203291, true); + alternative_587203267[0] = tree__dochack_u130("DIV", [x_587203292]); + } + + if ((value_p0 == "type")) { + replaceById__dochack_u158("tocRoot", alternative_587203267[0]); + } + else { + replaceById__dochack_u158("tocRoot", tree__dochack_u130("DIV", [])); + } + + togglevis__dochack_u708(document.getElementById("toc-list")); + + +} + +function raiseRangeError() { + raiseException({message: [118,97,108,117,101,32,111,117,116,32,111,102,32,114,97,110,103,101], parent: null, m_type: NTI134217751, name: null, trace: [], up: null}, "RangeDefect"); + + +} + +function HEX5BHEX5D__pureZstrutils_u1308(s_p0, x_p1) { + var result_754976033 = []; + + var a_754976035 = x_p1.a; + var L_754976037 = addInt(subInt(subInt((s_p0).length, x_p1.b), a_754976035), 1); + result_754976033 = nimCopy(null, mnewString(chckRange(L_754976037, 0, 2147483647)), NTI33554449); + Label1: { + var i_754976042 = 0; + var i_587203862 = 0; + Label2: { + Label3: while (true) { + if (!(i_587203862 < L_754976037)) break Label3; + i_754976042 = i_587203862; + result_754976033[chckIndx(i_754976042, 0, (result_754976033).length - 1)] = s_p0[chckIndx(addInt(i_754976042, a_754976035), 0, (s_p0).length - 1)]; + i_587203862 = addInt(i_587203862, 1); + } + }; + }; + + return result_754976033; + +} + +function HEX2EHEX2E__stdZenumutils_u105(a_p0, b_p1) { + var result_973078640 = ({a: 0, b: 0}); + + result_973078640 = nimCopy(result_973078640, {a: a_p0, b: b_p1}, NTI973078617); + + return result_973078640; + +} +async function loadIndex__dochack_u928() { + var result_587203490 = null; + + BeforeRet: { + var indexURL_587203496 = document.getElementById("indexLink").getAttribute("href"); + var rootURL_587203522 = HEX5BHEX5D__pureZstrutils_u1308(cstrToNimstr(indexURL_587203496), HEX2EHEX2E__stdZenumutils_u105(0, 14)); + var resp_587203534 = (await (await fetch(indexURL_587203496)).text()); + var indexElem_587203535 = document.createElement("div"); + indexElem_587203535.innerHTML = resp_587203534; + Label1: { + var href_587203557 = null; + var colontmp__587203856 = []; + colontmp__587203856 = indexElem_587203535.getElementsByClassName("reference"); + var i_587203858 = 0; + var L_587203859 = (colontmp__587203856).length; + Label2: { + Label3: while (true) { + if (!(i_587203858 < L_587203859)) break Label3; + href_587203557 = colontmp__587203856[chckIndx(i_587203858, 0, (colontmp__587203856).length - 1)]; + href_587203557.setAttribute("href", toJSStr((rootURL_587203522).concat(cstrToNimstr(href_587203557.getAttribute("href"))))); + db_587203309[0].push(href_587203557);; + contents_587203310[0].push(href_587203557.getAttribute("data-doc-search-tag"));; + i_587203858 += 1; + if (!(((colontmp__587203856).length == L_587203859))) { + failedAssertImpl__stdZassertions_u86(makeNimstrLit("iterators.nim(254, 11) `len(a) == L` the length of the seq changed while iterating over it")); + } + + } + }; + }; + result_587203490 = undefined; + break BeforeRet; + }; + + return result_587203490; + +} + +function then__dochack_u1107(future_p0, onSuccess_p1, onReject_p2) { + var result_587203673 = null; + + BeforeRet: { + var ret_587203683 = null; + ret_587203683 = future_p0.then(onSuccess_p1, onReject_p2); + result_587203673 = ret_587203683; + break BeforeRet; + }; + + return result_587203673; + +} + +function nsuToLowerAsciiChar(c_p0) { + var result_754974807 = 0; + + if ((ConstSet2[c_p0] != undefined)) { + result_754974807 = (c_p0 ^ 32); + } + else { + result_754974807 = c_p0; + } + + + return result_754974807; + +} + +function fuzzyMatch__fuzzysearch_u16(pattern_p0, str_p1) { + var Temporary4; + var Temporary5; + var Temporary6; + var Temporary7; + var Temporary8; + + var result_738197525 = {Field0: 0, Field1: false}; + + var scoreState_738197526 = (-100); + var headerMatched_738197527 = false; + var unmatchedLeadingCharCount_738197528 = 0; + var consecutiveMatchCount_738197529 = 0; + var strIndex_738197530 = 0; + var patIndex_738197531 = 0; + var score_738197532 = 0; + Label1: { + Label2: while (true) { + if (!((strIndex_738197530 < ((str_p1) == null ? 0 : (str_p1).length)) && (patIndex_738197531 < ((pattern_p0) == null ? 0 : (pattern_p0).length)))) break Label2; + Label3: { + var patternChar_738197535 = nsuToLowerAsciiChar(pattern_p0.charCodeAt(chckIndx(patIndex_738197531, 0, (pattern_p0).length - 1))); + var strChar_738197536 = nsuToLowerAsciiChar(str_p1.charCodeAt(chckIndx(strIndex_738197530, 0, (str_p1).length - 1))); + if ((ConstSet3[patternChar_738197535] != undefined)) { + patIndex_738197531 = addInt(patIndex_738197531, 1); + break Label3; + } + + if ((ConstSet4[strChar_738197536] != undefined)) { + strIndex_738197530 = addInt(strIndex_738197530, 1); + break Label3; + } + + if ((!(headerMatched_738197527) && (strChar_738197536 == 58))) { + headerMatched_738197527 = true; + scoreState_738197526 = (-100); + score_738197532 = chckRange(Number(BigInt(Math.trunc(Math.floor((0.5 * score_738197532))))), (-2147483648), 2147483647); + patIndex_738197531 = 0; + strIndex_738197530 = addInt(strIndex_738197530, 1); + break Label3; + } + + if ((strChar_738197536 == patternChar_738197535)) { + switch (scoreState_738197526) { + case (-100): + case 20: + scoreState_738197526 = 10; + break; + case 0: + scoreState_738197526 = 5; + score_738197532 = addInt(score_738197532, scoreState_738197526); + break; + case 10: + case 5: + consecutiveMatchCount_738197529 = addInt(consecutiveMatchCount_738197529, 1); + scoreState_738197526 = 5; + score_738197532 = addInt(score_738197532, mulInt(5, consecutiveMatchCount_738197529)); + if ((scoreState_738197526 == 10)) { + score_738197532 = addInt(score_738197532, 10); + } + + var onBoundary_738197588 = (patIndex_738197531 == ((pattern_p0) == null ? -1 : (pattern_p0).length - 1)); + if ((!(onBoundary_738197588) && (strIndex_738197530 < ((str_p1) == null ? -1 : (str_p1).length - 1)))) { + var nextPatternChar_738197589 = nsuToLowerAsciiChar(pattern_p0.charCodeAt(chckIndx(addInt(patIndex_738197531, 1), 0, (pattern_p0).length - 1))); + var nextStrChar_738197590 = nsuToLowerAsciiChar(str_p1.charCodeAt(chckIndx(addInt(strIndex_738197530, 1), 0, (str_p1).length - 1))); + if (!!((ConstSet5[nextStrChar_738197590] != undefined))) Temporary4 = false; else { Temporary4 = !((nextStrChar_738197590 == nextPatternChar_738197589)); } onBoundary_738197588 = Temporary4; + } + + if (onBoundary_738197588) { + scoreState_738197526 = 20; + score_738197532 = addInt(score_738197532, scoreState_738197526); + } + + break; + case (-1): + case (-3): + if (!((ConstSet6[str_p1.charCodeAt(chckIndx(subInt(strIndex_738197530, 1), 0, (str_p1).length - 1))] != undefined))) Temporary5 = true; else { if (!(ConstSet7[str_p1.charCodeAt(chckIndx(subInt(strIndex_738197530, 1), 0, (str_p1).length - 1))] != undefined)) Temporary6 = false; else { Temporary6 = (ConstSet8[str_p1.charCodeAt(chckIndx(strIndex_738197530, 0, (str_p1).length - 1))] != undefined); } Temporary5 = Temporary6; } var isLeadingChar_738197614 = Temporary5; + if (isLeadingChar_738197614) { + scoreState_738197526 = 10; + } + else { + scoreState_738197526 = 0; + score_738197532 = addInt(score_738197532, scoreState_738197526); + } + + break; + } + patIndex_738197531 = addInt(patIndex_738197531, 1); + } + else { + switch (scoreState_738197526) { + case (-100): + scoreState_738197526 = (-3); + score_738197532 = addInt(score_738197532, scoreState_738197526); + break; + case 5: + scoreState_738197526 = (-1); + score_738197532 = addInt(score_738197532, scoreState_738197526); + consecutiveMatchCount_738197529 = 0; + break; + case (-3): + if ((unmatchedLeadingCharCount_738197528 < 3)) { + scoreState_738197526 = (-3); + score_738197532 = addInt(score_738197532, scoreState_738197526); + } + + unmatchedLeadingCharCount_738197528 = addInt(unmatchedLeadingCharCount_738197528, 1); + break; + default: + scoreState_738197526 = (-1); + score_738197532 = addInt(score_738197532, scoreState_738197526); + break; + } + } + + strIndex_738197530 = addInt(strIndex_738197530, 1); + }; + } + }; + if (!(patIndex_738197531 == ((pattern_p0) == null ? 0 : (pattern_p0).length))) Temporary7 = false; else { if ((strIndex_738197530 == ((str_p1) == null ? 0 : (str_p1).length))) Temporary8 = true; else { Temporary8 = !((ConstSet9[str_p1.charCodeAt(chckIndx(strIndex_738197530, 0, (str_p1).length - 1))] != undefined)); } Temporary7 = Temporary8; } if (Temporary7) { + score_738197532 = addInt(score_738197532, 10); + } + + var colontmp__587203875 = nimMax(0, score_738197532); + var colontmp__587203876 = (0 < score_738197532); + result_738197525 = nimCopy(result_738197525, {Field0: colontmp__587203875, Field1: colontmp__587203876}, NTI738197518); + + return result_738197525; + +} + +function escapeCString__dochack_u751(x_p0, x_p0_Idx) { + var s_587203313 = []; + Label1: { + var c_587203314 = 0; + var iHEX60gensym13_587203879 = 0; + var nHEX60gensym13_587203880 = ((x_p0[x_p0_Idx]) == null ? 0 : (x_p0[x_p0_Idx]).length); + Label2: { + Label3: while (true) { + if (!(iHEX60gensym13_587203879 < nHEX60gensym13_587203880)) break Label3; + c_587203314 = x_p0[x_p0_Idx].charCodeAt(chckIndx(iHEX60gensym13_587203879, 0, (x_p0[x_p0_Idx]).length - 1)); + switch (c_587203314) { + case 60: + nimAddStrStr(s_587203313, [38,108,116,59]);; + break; + case 62: + nimAddStrStr(s_587203313, [38,103,116,59]);; + break; + default: + addChar(s_587203313, c_587203314);; + break; + } + iHEX60gensym13_587203879 += 1; + } + }; + }; + x_p0[x_p0_Idx] = toJSStr(s_587203313); + + +} + +function dosearch__dochack_u755(value_p0) { + var Temporary5; + +function HEX3Aanonymous__dochack_u783(a_p0, b_p1) { + var result_587203356 = 0; + + result_587203356 = subInt(b_p1["Field1"], a_p0["Field1"]); + + return result_587203356; + +} + + var result_587203317 = null; + + BeforeRet: { + if (((db_587203309[0]).length == 0)) { + break BeforeRet; + } + + var ul_587203321 = tree__dochack_u130("UL", []); + result_587203317 = tree__dochack_u130("DIV", []); + setClass__dochack_u152(result_587203317, "search_results"); + var matches_587203326 = []; + Label1: { + var i_587203334 = 0; + var colontmp__587203866 = 0; + colontmp__587203866 = (db_587203309[0]).length; + var i_587203867 = 0; + Label2: { + Label3: while (true) { + if (!(i_587203867 < colontmp__587203866)) break Label3; + i_587203334 = i_587203867; + Label4: { + var c_587203335 = contents_587203310[0][chckIndx(i_587203334, 0, (contents_587203310[0]).length - 1)]; + if (((c_587203335 == "Examples") || (c_587203335 == "PEG construction"))) { + break Label4; + } + + var tmpTuple_587203336 = fuzzyMatch__fuzzysearch_u16(value_p0, c_587203335); + var score_587203337 = tmpTuple_587203336["Field0"]; + var matched_587203338 = tmpTuple_587203336["Field1"]; + if (matched_587203338) { + matches_587203326.push({Field0: db_587203309[0][chckIndx(i_587203334, 0, (db_587203309[0]).length - 1)], Field1: score_587203337});; + } + + }; + i_587203867 = addInt(i_587203867, 1); + } + }; + }; + Temporary5 = HEX3Aanonymous__dochack_u783.bind(null); Temporary5.ClP_0 = HEX3Aanonymous__dochack_u783; Temporary5.ClE_0 = null; + matches_587203326.sort(Temporary5); + Label6: { + var i_587203373 = 0; + var colontmp__587203870 = 0; + colontmp__587203870 = nimMin((matches_587203326).length, 29); + var i_587203871 = 0; + Label7: { + Label8: while (true) { + if (!(i_587203871 < colontmp__587203870)) break Label8; + i_587203373 = i_587203871; + matches_587203326[chckIndx(i_587203373, 0, (matches_587203326).length - 1)]["Field0"].innerHTML = matches_587203326[chckIndx(i_587203373, 0, (matches_587203326).length - 1)]["Field0"].getAttribute("data-doc-search-tag"); + escapeCString__dochack_u751(matches_587203326[chckIndx(i_587203373, 0, (matches_587203326).length - 1)]["Field0"], "innerHTML"); + add__dochack_u148(ul_587203321, tree__dochack_u130("LI", [matches_587203326[chckIndx(i_587203373, 0, (matches_587203326).length - 1)]["Field0"]])); + i_587203871 = addInt(i_587203871, 1); + } + }; + }; + if ((ul_587203321.childNodes.length == 0)) { + add__dochack_u148(result_587203317, tree__dochack_u130("B", [text__dochack_u155("no search results")])); + } + else { + add__dochack_u148(result_587203317, tree__dochack_u130("B", [text__dochack_u155("search results")])); + add__dochack_u148(result_587203317, ul_587203321); + } + + }; + + return result_587203317; + +} + +function hideSearch__dochack_u1090() { + if (!((oldtoc_587203646[0] == null))) { + replaceById__dochack_u158("tocRoot", oldtoc_587203646[0]); + } + + + +} + +function runSearch__dochack_u1094() { + var elem_587203655 = document.getElementById("searchInput"); + var value_587203656 = elem_587203655.value; + if (!((value_587203656 == ""))) { + if ((oldtoc_587203646[0] == null)) { + oldtoc_587203646[0] = document.getElementById("tocRoot"); + } + + var results_587203660 = dosearch__dochack_u755(value_587203656); + replaceById__dochack_u158("tocRoot", results_587203660); + } + else { + hideSearch__dochack_u1090(); + } + + + +} + +function search() { + var Temporary1; + + if ((loadIndexFut_587203649[0] == null)) { + loadIndexFut_587203649[0] = loadIndex__dochack_u928(); + (then__dochack_u1107(loadIndexFut_587203649[0], runSearch__dochack_u1094, null)); + } + + if (!((timer_587203647[0] == null))) { + clearTimeout(timer_587203647[0]); + } + + Temporary1 = runSearch__dochack_u1094.bind(null); Temporary1.ClP_0 = runSearch__dochack_u1094; Temporary1.ClE_0 = null; + timer_587203647[0] = setTimeout(Temporary1, 400); + + +} + +function copyToClipboard() { + + function updatePreTags() { + + const allPreTags = document.querySelectorAll("pre:not(.line-nums)") + + allPreTags.forEach((e) => { + + const div = document.createElement("div") + div.classList.add("copyToClipBoard") + + const preTag = document.createElement("pre") + preTag.innerHTML = e.innerHTML + + const button = document.createElement("button") + button.value = e.textContent.replace('...', '') + button.classList.add("copyToClipBoardBtn") + button.style.cursor = "pointer" + + div.appendChild(preTag) + div.appendChild(button) + + e.outerHTML = div.outerHTML + + }) + } + + + function copyTextToClipboard(e) { + const clipBoardContent = e.target.value + navigator.clipboard.writeText(clipBoardContent).then(function() { + e.target.style.setProperty("--clipboard-image", "var(--clipboard-image-selected)") + }, function(err) { + console.error("Could not copy text: ", err); + }); + } + + window.addEventListener("click", (e) => { + if (e.target.classList.contains("copyToClipBoardBtn")) { + copyTextToClipboard(e) + } + }) + + window.addEventListener("mouseover", (e) => { + if (e.target.nodeName === "PRE") { + e.target.nextElementSibling.style.setProperty("--clipboard-image", "var(--clipboard-image-normal)") + } + }) + + window.addEventListener("DOMContentLoaded", updatePreTags) + + + + +} +var Temporary1; +var Temporary2; + +function HEX3Aanonymous__dochack_u1175(e_p0) { + if ((e_p0.key == "/")) { + e_p0.preventDefault(); + var searchElem_587203737 = document.getElementById("searchInput"); + searchElem_587203737.focus(); + searchElem_587203737.parentElement.scrollIntoView(); + runSearch__dochack_u1094(); + } + + + +} +var Temporary3; + +function HEX3Aanonymous__dochack_u1210(e_p0) { + hideSearch__dochack_u1090(); + + +} +var Temporary4; +var t_587202599 = window.localStorage.getItem("theme"); +if ((t_587202599 == null)) { +Temporary1 = "auto"; +} +else { +Temporary1 = t_587202599; +} + +setTheme(Temporary1); +var alternative_587203267 = [null]; +var db_587203309 = [[]]; +var contents_587203310 = [[]]; +var oldtoc_587203646 = [null]; +var timer_587203647 = [null]; +var loadIndexFut_587203649 = [null]; +Temporary2 = HEX3Aanonymous__dochack_u1175.bind(null); Temporary2.ClP_0 = HEX3Aanonymous__dochack_u1175; Temporary2.ClE_0 = null; +window.addEventListener("keypress", Temporary2, false); +Temporary3 = HEX3Aanonymous__dochack_u1210.bind(null); Temporary3.ClP_0 = HEX3Aanonymous__dochack_u1210; Temporary3.ClE_0 = null; +window.addEventListener("hashchange", Temporary3, false); +copyToClipboard(); +Temporary4 = onDOMLoaded.bind(null); Temporary4.ClP_0 = onDOMLoaded; Temporary4.ClE_0 = null; +window.addEventListener("DOMContentLoaded", Temporary4, false); diff --git a/clients/nim-allographer/docs/v2/env.html b/clients/nim-allographer/docs/v2/env.html new file mode 100644 index 0000000..3c4930e --- /dev/null +++ b/clients/nim-allographer/docs/v2/env.html @@ -0,0 +1,135 @@ + + + + + + + +src/allographer/env + + + + + + + + + + + + + + + + +
+
+

src/allographer/env

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Consts

+
+
+
isExistsMariadb = true
+
+ + + +
+
+
+
isExistsMysql = true
+
+ + + +
+
+
+
isExistsPostgres = true
+
+ + + +
+
+
+
isExistsSqlite = true
+
+ + + +
+
+
+
isExistsSurrealdb = true
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/env.idx b/clients/nim-allographer/docs/v2/env.idx new file mode 100644 index 0000000..f536a1e --- /dev/null +++ b/clients/nim-allographer/docs/v2/env.idx @@ -0,0 +1,6 @@ +nimTitle env env.html module src/allographer/env 0 +nim isExistsSqlite env.html#isExistsSqlite const isExistsSqlite 22 +nim isExistsPostgres env.html#isExistsPostgres const isExistsPostgres 23 +nim isExistsMysql env.html#isExistsMysql const isExistsMysql 24 +nim isExistsMariadb env.html#isExistsMariadb const isExistsMariadb 25 +nim isExistsSurrealdb env.html#isExistsSurrealdb const isExistsSurrealdb 26 diff --git a/clients/nim-allographer/docs/v2/nimdoc.out.css b/clients/nim-allographer/docs/v2/nimdoc.out.css new file mode 100644 index 0000000..1ca55a2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/nimdoc.out.css @@ -0,0 +1,1049 @@ +/* +Stylesheet for use with Docutils/rst2html. + +See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to +customize this style sheet. + +Modified from Chad Skeeters' rst2html-style +https://bitbucket.org/cskeeters/rst2html-style/ + +Modified by Boyd Greenfield and narimiran +*/ + +:root { + color-scheme: light; + --primary-background: #fff; + --secondary-background: ghostwhite; + --third-background: #e8e8e8; + --info-background: #50c050; + --warning-background: #c0a000; + --error-background: #e04040; + --border: #dde; + --text: #222; + --anchor: #07b; + --anchor-focus: #607c9f; + --input-focus: #1fa0eb; + --strong: #3c3c3c; + --hint: #9A9A9A; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1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"); + + --keyword: #5e8f60; + --identifier: #222; + --comment: #484a86; + --operator: #155da4; + --punctuation: black; + --other: black; + --escapeSequence: #c4891b; + --number: #252dbe; + --literal: #a4255b; + --program: #6060c0; + --option: #508000; + --raw-data: #a4255b; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal) +} + +[data-theme="dark"] { + color-scheme: dark; + --primary-background: #171921; + --secondary-background: #1e202a; + --third-background: #2b2e3b; + --info-background: #008000; + --warning-background: #807000; + --error-background: #c03000; + --border: #0e1014; + --text: #fff; + --anchor: #8be9fd; + --anchor-focus: #8be9fd; + --input-focus: #8be9fd; + --strong: #bd93f9; + --hint: #7A7C85; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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"); + + --keyword: #ff79c6; + --identifier: #f8f8f2; + --comment: #6272a4; + --operator: #ff79c6; + --punctuation: #f8f8f2; + --other: #f8f8f2; + --escapeSequence: #bd93f9; + --number: #bd93f9; + --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; + --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); +} + +@media (prefers-color-scheme: dark) { + [data-theme="auto"] { + color-scheme: dark; + --primary-background: #171921; + --secondary-background: #1e202a; + --third-background: #2b2e3b; + --info-background: #008000; + --warning-background: #807000; + --error-background: #c03000; + --border: #0e1014; + --text: #fff; + --anchor: #8be9fd; + --anchor-focus: #8be9fd; + --input-focus: #8be9fd; + --strong: #bd93f9; + --hint: #7A7C85; + --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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"); + + --keyword: #ff79c6; + --identifier: #f8f8f2; + --comment: #6272a4; + --operator: #ff79c6; + --punctuation: #f8f8f2; + --other: #f8f8f2; + --escapeSequence: #bd93f9; + --number: #bd93f9; + --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; + --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); + } +} + +.theme-select-wrapper { + display: flex; + align-items: center; +} + +html { + overflow-x: hidden; + max-width: 100%; + box-sizing: border-box; + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; } + +body { + max-width: 100%; + box-sizing: border-box; + font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; + font-weight: 400; + font-size: 1.125em; + line-height: 1.5; + color: var(--text); + background-color: var(--primary-background); } + +/* Skeleton grid */ +.container { + position: relative; + width: 100%; + max-width: 1050px; + margin: 0 auto; + padding: 0; + box-sizing: border-box; } + +.column, .columns { + width: 100%; + float: left; + box-sizing: border-box; + margin-left: 1%; } + +@media print { + #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby { + display:none; + } + .columns { + width:100% !important; + } +} + +.column:first-child, .columns:first-child { + margin-left: 0; } + +.container .row { + display: flex; } + +.three.columns { + width: 25.0%; + height: 100vh; + position: sticky; + top: 0px; + overflow-y: auto; + padding: 2px; +} + +.nine.columns { + width: 75.0%; + margin-left: 0; + padding-left: 1.5em; } + +.twelve.columns { + width: 100%; + margin-left: 0; } + +@media screen and (max-width: 860px) { + .three.columns { + display: none; + } + .nine.columns { + width: 100%; + margin-left: 0; + padding-left: 0; + } + body { + font-size: 1em; + line-height: 1.35; + } +} + +cite { + font-style: italic !important; } + + +/* Nim search input */ +div#searchInputDiv { + margin-bottom: 1em; +} +input#searchInput { + width: 80%; +} + +/* + * Some custom formatting for input forms. + * This also fixes input form colors on Firefox with a dark system theme on Linux. + */ +input { + -moz-appearance: none; + background-color: var(--secondary-background); + color: var(--text); + border: 1px solid var(--border); + font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; + font-size: 0.9em; + padding: 6px; +} + +input:focus { + border: 1px solid var(--input-focus); + box-shadow: 0 0 3px var(--input-focus); +} + +select { + -moz-appearance: none; + background-color: var(--secondary-background); + color: var(--text); + border: 1px solid var(--border); + font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif; + font-size: 0.9em; + padding: 6px; +} + +select:focus { + border: 1px solid var(--input-focus); + box-shadow: 0 0 3px var(--input-focus); +} + +/* Docgen styles */ + +:target { + border: 2px solid #B5651D; + border-style: dotted; +} + +/* Links */ +a { + color: var(--anchor); + text-decoration: none; +} + +a span.Identifier { + text-decoration: underline; + text-decoration-color: #aab; +} + +a.reference-toplevel { + font-weight: bold; +} + +a.nimdoc { + word-spacing: 0.3em; +} + +a.toc-backref { + text-decoration: none; + color: var(--text); +} + +a.link-seesrc { + color: #607c9f; + font-size: 0.9em; + font-style: italic; +} + +a:hover, a:focus { + color: var(--anchor-focus); + text-decoration: underline; +} + +a:hover span.Identifier { + color: var(--anchor); +} + + +sub, sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; } + +sup { + top: -0.5em; } + +sub { + bottom: -0.25em; } + +img { + width: auto; + height: auto; + max-width: 100%; + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; } + +@media print { + * { + color: black !important; + text-shadow: none !important; + background: transparent !important; + box-shadow: none !important; } + + a, a:visited { + text-decoration: underline; } + + a[href]:after { + content: " (" attr(href) ")"; } + + abbr[title]:after { + content: " (" attr(title) ")"; } + + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; } + + pre, blockquote { + border: 1px solid #999; + page-break-inside: avoid; } + + thead { + display: table-header-group; } + + tr, img { + page-break-inside: avoid; } + + img { + max-width: 100% !important; } + + @page { + margin: 0.5cm; } + + h1 { + page-break-before: always; } + + h1.title { + page-break-before: avoid; } + + p, h2, h3 { + orphans: 3; + widows: 3; } + + h2, h3 { + page-break-after: avoid; } +} + + +p { + margin-top: 0.5em; + margin-bottom: 0.5em; } + +small { + font-size: 85%; } + +strong { + font-weight: 600; + font-size: 0.95em; + color: var(--strong); } + +em { + font-style: italic; } + +h1 { + font-size: 1.8em; + font-weight: 400; + padding-bottom: .25em; + border-bottom: 6px solid var(--third-background); + margin-top: 2.5em; + margin-bottom: 1em; + line-height: 1.2em; } + +h1.title { + padding-bottom: 1em; + border-bottom: 0px; + font-size: 2.5em; + text-align: center; + font-weight: 900; + margin-top: 0.75em; + margin-bottom: 0em; } + +h2 { + font-size: 1.3em; + margin-top: 2em; } + +h2.subtitle { + margin-top: 0em; + text-align: center; } + +h3 { + font-size: 1.125em; + font-style: italic; + margin-top: 1.5em; } + +h4 { + font-size: 1.125em; + margin-top: 1em; } + +h5 { + font-size: 1.125em; + margin-top: 0.75em; } + +h6 { + font-size: 1.1em; } + + +ul, ol { + padding: 0; + margin-top: 0.5em; + margin-left: 0.75em; } + +ul ul, ul ol, ol ol, ol ul { + margin-bottom: 0; + margin-left: 1.25em; } + +ul.simple > li { + list-style-type: circle; } + +ul.simple-boot li { + list-style-type: none; + margin-left: 0em; + margin-bottom: 0.5em; } + +ol.simple > li, ul.simple > li { + margin-bottom: 0.2em; + margin-left: 0.4em } + +ul.simple.simple-toc > li { + margin-top: 1em; } + +ul.simple-toc { + list-style: none; + font-size: 0.9em; + margin-left: -0.3em; + margin-top: 1em; } + +ul.simple-toc > li { + list-style-type: none; } + +ul.simple-toc-section { + list-style-type: circle; + margin-left: 0.8em; + color: #6c9aae; } + +ul.nested-toc-section { + list-style-type: circle; + margin-left: -0.75em; + color: var(--text); } + +ul.nested-toc-section > li { + margin-left: 1.25em; } + + +ol.arabic { + list-style: decimal; } + +ol.loweralpha { + list-style: lower-alpha; } + +ol.upperalpha { + list-style: upper-alpha; } + +ol.lowerroman { + list-style: lower-roman; } + +ol.upperroman { + list-style: upper-roman; } + +ul.auto-toc { + list-style-type: none; } + + +dl { + margin-bottom: 1.5em; } + +dt { + margin-bottom: -0.5em; + margin-left: 0.0em; } + +dd { + margin-left: 2.0em; + margin-bottom: 3.0em; + margin-top: 0.5em; } + + +hr { + margin: 2em 0; + border: 0; + border-top: 1px solid #aaa; } + +hr.footnote { + width: 25%; + border-top: 0.15em solid #999; + margin-bottom: 0.15em; + margin-top: 0.15em; +} +div.footnote-group { + margin-left: 1em; +} +div.footnote-label { + display: inline-block; + min-width: 1.7em; +} + +div.option-list { + border: 0.1em solid var(--border); +} +div.option-list-item { + padding-left: 12em; + padding-right: 0; + padding-bottom: 0.3em; + padding-top: 0.3em; +} +div.odd { + background-color: var(--secondary-background); +} +div.option-list-label { + margin-left: -11.5em; + margin-right: 0em; + min-width: 11.5em; + display: inline-block; + vertical-align: top; +} +div.option-list-description { + width: calc(100% - 1em); + padding-left: 1em; + padding-right: 0; + display: inline-block; +} + +blockquote { + font-size: 0.9em; + font-style: italic; + padding-left: 0.5em; + margin-left: 0; + border-left: 5px solid #bbc; +} + +blockquote.markdown-quote { + font-size: 0.9rem; /* use rem to avoid recursion */ + font-style: normal; +} + +.pre, span.tok { + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + font-weight: 500; + font-size: 0.85em; + color: var(--text); + background-color: var(--third-background); + padding-left: 3px; + padding-right: 3px; + border-radius: 4px; + white-space: normal; + word-break: break-all; +} + +span.tok { + border: 1px solid #808080; + padding-bottom: 0.1em; + margin-right: 0.2em; +} + +.copyToClipBoard { + position: relative; +} + +pre { + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--text); + font-weight: 500; + display: inline-block; + box-sizing: border-box; + min-width: 100%; + max-width: 100%; + padding: 0.5em; + margin-top: 0.5em; + margin-bottom: 0.5em; + font-size: 0.85em; + white-space: pre !important; + overflow-y: hidden; + overflow-x: auto; + background-color: var(--secondary-background); + border: 1px solid var(--border); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +.copyToClipBoardBtn { + visibility: hidden; + position: absolute; + width: 24px; + border-radius: 4px; + background-image: var(--clipboard-image); + right: 5px; + top: 13px; + background-color: var(--secondary-background); + padding: 11px; + border: 0; +} + +.copyToClipBoard:hover .copyToClipBoardBtn { + visibility: visible; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; } + + +/* Nim line-numbered tables */ +.line-nums-table { + width: 100%; + table-layout: fixed; } + +table.line-nums-table { + border-radius: 4px; + border: 1px solid var(--border); + background-color: var(--secondary-background); + border-collapse: separate; + margin-top: 15px; + margin-bottom: 25px; } + +.line-nums-table tbody { + border: none; } + +.line-nums-table td pre { + border: none; + background-color: transparent; } + +.line-nums-table td.blob-line-nums { + width: 28px; } + +.line-nums-table td.blob-line-nums pre { + color: #b0b0b0; + -webkit-filter: opacity(75%); + filter: opacity(75%); + text-align: right; + border-color: transparent; + background-color: transparent; + padding-left: 0px; + margin-left: 0px; + padding-right: 0px; + margin-right: 0px; } + + +table { + max-width: 100%; + background-color: transparent; + margin-top: 0.5em; + margin-bottom: 1.5em; + border-collapse: collapse; + border-color: var(--third-background); + border-spacing: 0; +} + +table:not(.line-nums-table) { + font-size: 0.9em; +} + +table th, table td { + padding: 0px 0.5em 0px; + border-color: var(--third-background); +} + +table th { + background-color: var(--third-background); + border-color: var(--third-background); + font-weight: bold; } + +table th.docinfo-name { + background-color: transparent; + text-align: right; +} + +table:not(.line-nums-table) tr:hover { + background-color: var(--third-background); } + + +/* rst2html default used to remove borders from tables and images */ +.borderless, table.borderless td, table.borderless th { + border: 0; } + +table.borderless td, table.borderless th { + /* Override padding for "table.docutils td" with "! important". + The right padding separates the table cells. */ + padding: 0 0.5em 0 0 !important; } + +.admonition { + padding: 0.3em; + background-color: var(--secondary-background); + border-left: 0.4em solid #7f7f84; + margin-bottom: 0.5em; + -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); +} +.admonition-info { + border-color: var(--info-background); +} +.admonition-info-text { + color: var(--info-background); +} +.admonition-warning { + border-color: var(--warning-background); +} +.admonition-warning-text { + color: var(--warning-background); +} +.admonition-error { + border-color: var(--error-background); +} +.admonition-error-text { + color: var(--error-background); +} + +.first { + /* Override more specific margin styles with "! important". */ + margin-top: 0 !important; } + +.last, .with-subtitle { + margin-bottom: 0 !important; } + +.hidden { + display: none; } + +blockquote.epigraph { + margin: 2em 5em; } + +dl.docutils dd { + margin-bottom: 0.5em; } + +object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] { + overflow: hidden; } + + +div.figure { + margin-left: 2em; + margin-right: 2em; } + +div.footer, div.header { + clear: both; + text-align: center; + color: #666; + font-size: smaller; } + +div.footer { + padding-top: 5em; } + +div.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; } + +div.line-block div.line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; } + +div.topic { + margin: 2em; } + +div.search_results { + background-color: var(--third-background); + padding: 1em; + border: 1px solid #4d4d4d; + position: sticky; + top: 1em; + isolation: isolate; + max-width: calc(100vw - 6em); + z-index: 1; + max-height: calc(100vh - 6em); + overflow-y: scroll;} + +div#global-links ul { + margin-left: 0; + list-style-type: none; } + +div#global-links > simple-boot { + margin-left: 3em; } + +hr.docutils { + width: 75%; } + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; } + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; } + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; } + +.align-left { + text-align: left; } + +.align-center { + clear: both; + text-align: center; } + +.align-right { + text-align: right; } + +/* reset inner alignment in figures */ +div.align-right { + text-align: inherit; } + +p.attribution { + text-align: right; + margin-left: 50%; } + +p.caption { + font-style: italic; } + +p.credits { + font-style: italic; + font-size: smaller; } + +p.label { + white-space: nowrap; } + +p.rubric { + font-weight: bold; + font-size: larger; + color: maroon; + text-align: center; } + +p.topic-title { + font-weight: bold; } + +pre.address { + margin-bottom: 0; + margin-top: 0; + font: inherit; } + +pre.literal-block, pre.doctest-block, pre.math, pre.code { + margin-left: 2em; + margin-right: 2em; } + +pre.code .ln { + color: grey; } + +/* line numbers */ +pre.code, code { + background-color: #eeeeee; } + +pre.code .comment, code .comment { + color: #5c6576; } + +pre.code .keyword, code .keyword { + color: #3B0D06; + font-weight: bold; } + +pre.code .literal.string, code .literal.string { + color: #0c5404; } + +pre.code .name.builtin, code .name.builtin { + color: #352b84; } + +pre.code .deleted, code .deleted { + background-color: #DEB0A1; } + +pre.code .inserted, code .inserted { + background-color: #A3D289; } + +span.classifier { + font-style: oblique; } + +span.classifier-delimiter { + font-weight: bold; } + +span.problematic { + color: #b30000; } + +span.section-subtitle { + /* font-size relative to parent (h1..h6 element) */ + font-size: 80%; } + +span.DecNumber { + color: var(--number); } + +span.BinNumber { + color: var(--number); } + +span.HexNumber { + color: var(--number); } + +span.OctNumber { + color: var(--number); } + +span.FloatNumber { + color: var(--number); } + +span.Identifier { + color: var(--identifier); } + +span.Keyword { + font-weight: 600; + color: var(--keyword); } + +span.StringLit { + color: var(--literal); } + +span.LongStringLit { + color: var(--literal); } + +span.CharLit { + color: var(--literal); } + +span.EscapeSequence { + color: var(--escapeSequence); } + +span.Operator { + color: var(--operator); } + +span.Punctuation { + color: var(--punctuation); } + +span.Comment, span.LongComment { + font-style: italic; + font-weight: 400; + color: var(--comment); } + +span.RegularExpression { + color: darkviolet; } + +span.TagStart { + color: darkviolet; } + +span.TagEnd { + color: darkviolet; } + +span.Key { + color: #252dbe; } + +span.Value { + color: #252dbe; } + +span.RawData { + color: var(--raw-data); } + +span.Assembler { + color: #252dbe; } + +span.Preprocessor { + color: #252dbe; } + +span.Directive { + color: #252dbe; } + +span.option { + font-weight: bold; + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--option); } + +span.Prompt { + font-weight: bold; + color: red; } + +span.ProgramOutput { + font-weight: bold; + color: #808080; } + +span.program { + font-weight: bold; + color: var(--program); + text-decoration: underline; + text-decoration-color: var(--hint); + text-decoration-thickness: 0.05em; + text-underline-offset: 0.15em; } + +span.Command, span.Rule, span.Hyperlink, +span.Label, span.Reference, span.Other { + color: var(--other); } + +/* Pop type, const, proc, and iterator defs in nim def blocks */ +dt pre > span.Identifier, dt pre > span.Operator { + color: var(--identifier); + font-weight: 700; } + +dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier, +dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier { + color: var(--identifier); + font-weight: inherit; } + +/* Nim sprite for the footer (taken from main page favicon) */ +.nim-sprite { + display: inline-block; + width: 51px; + height: 14px; + background-position: 0 0; + background-size: 51px 14px; + -webkit-filter: opacity(50%); + filter: opacity(50%); + background-repeat: no-repeat; + background-image: var(--nim-sprite-base64); + margin-bottom: 5px; } + +span.pragmadots { + /* Position: relative frees us up to make the dots + look really nice without fucking up the layout and + causing bulging in the parent container */ + position: relative; + /* 1px down looks slightly nicer */ + top: 1px; + padding: 2px; + background-color: var(--third-background); + border-radius: 4px; + margin: 0 2px; + cursor: pointer; + font-size: 0.8em; } + +span.pragmadots:hover { + background-color: var(--hint); } + +span.pragmawrap { + display: none; } + +span.attachedType { + display: none; + visibility: hidden; } diff --git a/clients/nim-allographer/docs/v2/query_builder.html b/clients/nim-allographer/docs/v2/query_builder.html new file mode 100644 index 0000000..4c3cbdb --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder.html @@ -0,0 +1,94 @@ + + + + + + + +src/allographer/query_builder + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Exports

+
+ Numeric, None, Order, Collate, Asc, Desc, Collation, DbError, dbError, orm, orm, $, Connections, RawSqliteQuery, SqliteQuery, Connection, SqliteConnections, SQLite3, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, avg, insert, max, findPlain, firstPlain, first, sum, exec, find, count, get, rollback, update, delete, insert, findPlain, getPlain, begin, insertId, getPlain, insert, columns, insertId, insertId, get, firstPlain, first, update, seeder, min, delete, seeder, insertId, commit, find, insert, transaction, $, RawPostgresQuery, isConnected, PostgreSQL, Connections, PostgresQuery, Connection, PostgresConnections, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, avg, insert, max, findPlain, firstPlain, first, sum, exec, find, count, get, rollback, update, delete, insert, findPlain, getPlain, begin, insertId, getPlain, insert, columns, insertId, insertId, get, firstPlain, first, update, seeder, min, delete, seeder, insertId, commit, find, insert, transaction, $, MariaDB, Connections, ConnectionInfo, isConnected, MariadbQuery, RawMariadbQuery, MariadbConnections, Connection, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, avg, insert, max, findPlain, firstPlain, first, sum, exec, find, count, get, rollback, update, delete, insert, findPlain, getPlain, begin, insertId, getPlain, insert, columns, insertId, insertId, get, firstPlain, first, update, seeder, min, delete, seeder, insertId, commit, find, insert, transaction, $, MySQL, MysqlConnections, Connections, RawMysqlQuery, ConnectionInfo, isConnected, MysqlQuery, Connection, distinct, leftJoin, whereBetween, having, table, whereNull, whereNotBetween, groupBy, join, select, limit, whereBetween, whereIn, raw, offset, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, where, avg, insert, max, findPlain, firstPlain, first, sum, exec, find, count, get, rollback, update, delete, insert, findPlain, getPlain, begin, insertId, getPlain, insert, columns, insertId, insertId, get, firstPlain, first, update, seeder, min, delete, seeder, insertId, commit, find, insert, transaction, $, $, new, rawId, new, SurrealId, new, RawSurrealQuery, SurrealDB, %, Connections, Connection, new, SurrealQuery, SurrealConnections, distinct, limit, parallel, having, table, whereNull, whereIn, groupBy, select, whereBetween, orderBy, fetch, raw, orderBy, where, whereNotBetween, orWhere, whereNotIn, having, orWhere, where, table, start, min, max, first, sum, exec, count, update, update, delete, insertId, get, update, insertId, insertId, columns, insert, find, get, insert, first, update, info, seeder, avg, delete, seeder, insert, insert, insertId +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder.idx b/clients/nim-allographer/docs/v2/query_builder.idx new file mode 100644 index 0000000..c9deaf4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder.idx @@ -0,0 +1 @@ +nimTitle query_builder query_builder.html module src/allographer/query_builder 0 diff --git a/clients/nim-allographer/docs/v2/query_builder/enums.html b/clients/nim-allographer/docs/v2/query_builder/enums.html new file mode 100644 index 0000000..041c73a --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/enums.html @@ -0,0 +1,112 @@ + + + + + + + +src/allographer/query_builder/enums + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/enums

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
Collation = enum
+  None = "", Collate = "COLLATE", Numeric = "NUMERIC"
+
+ + + +
+
+
+
Order = enum
+  Asc = "ASC", Desc = "DESC"
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/enums.idx b/clients/nim-allographer/docs/v2/query_builder/enums.idx new file mode 100644 index 0000000..af3e599 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/enums.idx @@ -0,0 +1,8 @@ +nimTitle enums query_builder/enums.html module src/allographer/query_builder/enums 0 +nim Asc query_builder/enums.html#Asc Order.Asc 1 +nim Desc query_builder/enums.html#Desc Order.Desc 1 +nim Order query_builder/enums.html#Order enum Order 1 +nim None query_builder/enums.html#None Collation.None 5 +nim Collate query_builder/enums.html#Collate Collation.Collate 5 +nim Numeric query_builder/enums.html#Numeric Collation.Numeric 5 +nim Collation query_builder/enums.html#Collation enum Collation 5 diff --git a/clients/nim-allographer/docs/v2/query_builder/error.html b/clients/nim-allographer/docs/v2/query_builder/error.html new file mode 100644 index 0000000..7f67741 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/error.html @@ -0,0 +1,129 @@ + + + + + + + +src/allographer/query_builder/error + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/error

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
DbError = object of IOError
+
+ + exception that is raised if a database error occurs + +
+
+ +
+
+
+

Procs

+
+
+
+
proc dbError(msg: string) {.noreturn, noinline, ...raises: [DbError], tags: [],
+                            forbids: [].}
+
+ + raises an DbError exception with message msg. + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/error.idx b/clients/nim-allographer/docs/v2/query_builder/error.idx new file mode 100644 index 0000000..95de0f5 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/error.idx @@ -0,0 +1,3 @@ +nimTitle error query_builder/error.html module src/allographer/query_builder/error 0 +nim DbError query_builder/error.html#DbError object DbError 2 +nim dbError query_builder/error.html#dbError,string proc dbError(msg: string) 4 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/database_url.html b/clients/nim-allographer/docs/v2/query_builder/libs/database_url.html new file mode 100644 index 0000000..c85af69 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/database_url.html @@ -0,0 +1,274 @@ + + + + + + + +src/allographer/query_builder/libs/database_url + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/database_url

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
DatabaseUrl = distinct string
+
+ + + +
+
+
+
DatabaseUrlQuery = tuple[key, value: string]
+
+ + + +
+
+
+
ParsedDatabaseUrl = object
+  raw*: string
+  scheme*: string
+  username*: string
+  password*: string
+  hostname*: string
+  port*: int
+  hasPort*: bool
+  path*: string
+  query*: seq[DatabaseUrlQuery]
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc `$`(value: DatabaseUrl): string {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc asDatabaseUrl(value: string): DatabaseUrl {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc databaseName(value: ParsedDatabaseUrl): string {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc parseDatabaseUrl(value: DatabaseUrl): ParsedDatabaseUrl {.
+    ...raises: [ValueError], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc parseDatabaseUrl(value: string): ParsedDatabaseUrl {....raises: [ValueError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc portOrDefault(value: ParsedDatabaseUrl; defaultPort: int): int {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc requireDatabaseUrlScheme(value: ParsedDatabaseUrl;
+                              expectedSchemes: openArray[string];
+                              driverName: string) {....raises: [ValueError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sqliteDatabasePath(value: ParsedDatabaseUrl): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/database_url.idx b/clients/nim-allographer/docs/v2/query_builder/libs/database_url.idx new file mode 100644 index 0000000..2350f7f --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/database_url.idx @@ -0,0 +1,13 @@ +nimTitle database_url query_builder/libs/database_url.html module src/allographer/query_builder/libs/database_url 0 +nim DatabaseUrl query_builder/libs/database_url.html#DatabaseUrl type DatabaseUrl 5 +nim DatabaseUrlQuery query_builder/libs/database_url.html#DatabaseUrlQuery tuple DatabaseUrlQuery 7 +nim ParsedDatabaseUrl query_builder/libs/database_url.html#ParsedDatabaseUrl object ParsedDatabaseUrl 9 +nim asDatabaseUrl query_builder/libs/database_url.html#asDatabaseUrl,string proc asDatabaseUrl(value: string): DatabaseUrl 21 +nim `$` query_builder/libs/database_url.html#$,DatabaseUrl proc `$`(value: DatabaseUrl): string 25 +nim parseDatabaseUrl query_builder/libs/database_url.html#parseDatabaseUrl,string proc parseDatabaseUrl(value: string): ParsedDatabaseUrl 44 +nim parseDatabaseUrl query_builder/libs/database_url.html#parseDatabaseUrl,DatabaseUrl proc parseDatabaseUrl(value: DatabaseUrl): ParsedDatabaseUrl 60 +nim requireDatabaseUrlScheme query_builder/libs/database_url.html#requireDatabaseUrlScheme,ParsedDatabaseUrl,openArray[string],string proc requireDatabaseUrlScheme(value: ParsedDatabaseUrl;\n expectedSchemes: openArray[string]; driverName: string) 64 +nim databaseName query_builder/libs/database_url.html#databaseName,ParsedDatabaseUrl proc databaseName(value: ParsedDatabaseUrl): string 82 +nim sqliteDatabasePath query_builder/libs/database_url.html#sqliteDatabasePath,ParsedDatabaseUrl proc sqliteDatabasePath(value: ParsedDatabaseUrl): string 86 +nim portOrDefault query_builder/libs/database_url.html#portOrDefault,ParsedDatabaseUrl,int proc portOrDefault(value: ParsedDatabaseUrl; defaultPort: int): int 97 +nimgrp parsedatabaseurl query_builder/libs/database_url.html#parseDatabaseUrl-procs-all proc 44 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.html b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.html new file mode 100644 index 0000000..7cc2367 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.html @@ -0,0 +1,295 @@ + + + + + + + +src/allographer/query_builder/libs/mariadb/mariadb_impl + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/mariadb/mariadb_impl

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(db: PMySQL; query: string; args: JsonNode; columns: seq[seq[string]];
+          timeout: int): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, KeyError, ValueError, DbError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc exec(db: PMySQL; query: string; args: JsonNode; timeout: int): owned(
+    Future[void]) {....stackTrace: false,
+                    raises: [Exception, ValueError, DbError, OSError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc exec(db: PMySQL; query: string; args: seq[string]; timeout: int): owned(
+    Future[void]) {....stackTrace: false,
+                    raises: [Exception, ValueError, DbError, OSError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execGetValue(db: PMySQL; query: string; args: JsonNode;
+                  columns: seq[seq[string]]; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, DbError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getColumns(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[
+    seq[string]] {....stackTrace: false,
+                   raises: [Exception, ValueError, DbError, OSError],
+                   tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getColumnTypes(db: PMySQL; database, table: string; timeout: int): Future[
+    seq[database_types.Row]] {....stackTrace: false, raises: [Exception,
+    ValueError, DbError, OSError], tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc query(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, DbError, OSError, KeyError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc query(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, DbError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc queryPlain(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[
+    seq[database_types.Row]] {....stackTrace: false, raises: [Exception,
+    ValueError, DbError, OSError], tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc queryPlain(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[
+    seq[database_types.Row]] {....stackTrace: false, raises: [Exception,
+    ValueError, DbError, OSError], tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rawQuery(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, DbError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.idx b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.idx new file mode 100644 index 0000000..30e03e7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_impl.idx @@ -0,0 +1,15 @@ +nimTitle mariadb_impl query_builder/libs/mariadb/mariadb_impl.html module src/allographer/query_builder/libs/mariadb/mariadb_impl 0 +nim query query_builder/libs/mariadb/mariadb_impl.html#query,PMySQL,string,seq[string],int proc query(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 178 +nim query query_builder/libs/mariadb/mariadb_impl.html#query,PMySQL,string,JsonNode,int proc query(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 212 +nim queryPlain query_builder/libs/mariadb/mariadb_impl.html#queryPlain,PMySQL,string,seq[string],int proc queryPlain(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[\n seq[database_types.Row]] 215 +nim queryPlain query_builder/libs/mariadb/mariadb_impl.html#queryPlain,PMySQL,string,JsonNode,int proc queryPlain(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[\n seq[database_types.Row]] 241 +nim exec query_builder/libs/mariadb/mariadb_impl.html#exec,PMySQL,string,seq[string],int proc exec(db: PMySQL; query: string; args: seq[string]; timeout: int): owned(\n Future[void]) 244 +nim exec query_builder/libs/mariadb/mariadb_impl.html#exec,PMySQL,string,JsonNode,int proc exec(db: PMySQL; query: string; args: JsonNode; timeout: int): owned(\n Future[void]) 249 +nim exec query_builder/libs/mariadb/mariadb_impl.html#exec,PMySQL,string,JsonNode,seq[seq[string]],int proc exec(db: PMySQL; query: string; args: JsonNode; columns: seq[seq[string]];\n timeout: int): owned(Future[void]) 252 +nim execGetValue query_builder/libs/mariadb/mariadb_impl.html#execGetValue,PMySQL,string,JsonNode,seq[seq[string]],int proc execGetValue(db: PMySQL; query: string; args: JsonNode;\n columns: seq[seq[string]]; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 256 +nim rawQuery query_builder/libs/mariadb/mariadb_impl.html#rawQuery,PMySQL,string,JsonNode,int proc rawQuery(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 291 +nim getColumns query_builder/libs/mariadb/mariadb_impl.html#getColumns,PMySQL,string,seq[string],int proc getColumns(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[\n seq[string]] 294 +nim getColumnTypes query_builder/libs/mariadb/mariadb_impl.html#getColumnTypes,PMySQL,string,string,int proc getColumnTypes(db: PMySQL; database, table: string; timeout: int): Future[\n seq[database_types.Row]] 317 +nimgrp queryplain query_builder/libs/mariadb/mariadb_impl.html#queryPlain-procs-all proc 215 +nimgrp query query_builder/libs/mariadb/mariadb_impl.html#query-procs-all proc 178 +nimgrp exec query_builder/libs/mariadb/mariadb_impl.html#exec-procs-all proc 244 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.html b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.html new file mode 100644 index 0000000..f442d2d --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.html @@ -0,0 +1,207 @@ + + + + + + + +src/allographer/query_builder/libs/mariadb/mariadb_lib + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/mariadb/mariadb_lib

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
MariadbParams = seq[MariadbParamValue]
+
+ + + +
+
+
+
MariadbParamValue = object
+  value*: string
+  isBinary*: bool
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc dbError(db: PMySQL) {.noreturn, ...raises: [DbError], tags: [], forbids: [].}
+
+ + raises a DbError exception. + +
+
+ +
+
+
+
proc dbFormat(conn: PMySQL; query: string; args: MariadbParams): string {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc dbFormat(formatstr: string; args: seq[string]): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromObj(_: type MariadbParams; args: JsonNode; columns: seq[seq[string]]): MariadbParams
+
+ + args is JArray [{"key":"id", "value": 1}, {"key": "name" "value": "alice"}] + +
+
+ +
+
+
+
proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.idx b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.idx new file mode 100644 index 0000000..3ef3ea0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_lib.idx @@ -0,0 +1,9 @@ +nimTitle mariadb_lib query_builder/libs/mariadb/mariadb_lib.html module src/allographer/query_builder/libs/mariadb/mariadb_lib 0 +nim dbError query_builder/libs/mariadb/mariadb_lib.html#dbError,PMySQL proc dbError(db: PMySQL) 12 +nim setColumnInfo query_builder/libs/mariadb/mariadb_lib.html#setColumnInfo,DbColumns,PRES,int proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) 80 +nim dbFormat query_builder/libs/mariadb/mariadb_lib.html#dbFormat,string,seq[string] proc dbFormat(formatstr: string; args: seq[string]): string 120 +nim MariadbParamValue query_builder/libs/mariadb/mariadb_lib.html#MariadbParamValue object MariadbParamValue 171 +nim MariadbParams query_builder/libs/mariadb/mariadb_lib.html#MariadbParams type MariadbParams 175 +nim fromObj query_builder/libs/mariadb/mariadb_lib.html#fromObj,typeMariadbParams,JsonNode,seq[seq[string]] proc fromObj(_: type MariadbParams; args: JsonNode; columns: seq[seq[string]]): MariadbParams 177 +nim dbFormat query_builder/libs/mariadb/mariadb_lib.html#dbFormat,PMySQL,string,MariadbParams proc dbFormat(conn: PMySQL; query: string; args: MariadbParams): string 211 +nimgrp dbformat query_builder/libs/mariadb/mariadb_lib.html#dbFormat-procs-all proc 120 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.html b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.html new file mode 100644 index 0000000..511446d --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.html @@ -0,0 +1,1801 @@ + + + + + + + +src/allographer/query_builder/libs/mariadb/mariadb_rdb + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/mariadb/mariadb_rdb

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
BIND = St_mysql_bind
+
+ + + +
+
+
+
Charset_info_st {.final.} = object
+  number*: cuint
+  primary_number*: cuint
+  binary_number*: cuint
+  state*: cuint
+  csname*: cstring
+  name*: cstring
+  comment*: cstring
+  tailoring*: cstring
+  ftype*: cstring
+  to_lower*: cstring
+  to_upper*: cstring
+  sort_order*: cstring
+  contractions*: ptr int16
+  sort_order_big*: ptr ptr int16
+  tab_to_uni*: ptr int16
+  tab_from_uni*: pointer
+  state_map*: cstring
+  ident_map*: cstring
+  strxfrm_multiply*: cuint
+  mbminlen*: cuint
+  mbmaxlen*: cuint
+  min_sort_char*: int16
+  max_sort_char*: int16
+  escape_with_backslash_is_dangerous*: my_bool
+  cset*: pointer
+  coll*: pointer
+
+ + + +
+
+
+
cuint = cint
+
+ + + +
+
+
+
DATA = St_mysql_data
+
+ + + +
+
+
+
Enum_field_types = enum
+  TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE,
+  TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME,
+  TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT,
+  TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248, TYPE_TINY_BLOB = 249,
+  TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251, TYPE_BLOB = 252,
+  TYPE_VAR_STRING = 253, TYPE_STRING = 254, TYPE_GEOMETRY = 255
+
+ + + +
+
+
+
Enum_mysql_stmt_state = enum
+  STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE
+
+ + + +
+
+
+
Enum_server_command {....deprecated.} = enum
+  COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB,
+  COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO,
+  COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME,
+  COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP,
+  COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE,
+  COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION,
+  COM_STMT_FETCH, COM_END
+
+
+ Deprecated +
+ + + +
+
+
+
FIELD = St_mysql_field
+
+ + + +
+
+
+
gptr = cstring
+
+ + + +
+
+
+
MEM_ROOT = St_mem_root
+
+ + + +
+
+
+
my_bool = bool
+
+ + + +
+
+
+
my_socket = cint
+
+ + + +
+
+
+
my_ulonglong = int64
+
+ + + +
+
+
+
MySQL = St_mysql
+
+ + + +
+
+
+
NET = St_net
+
+ + + +
+
+
+
Option = enum
+  OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND,
+  READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME,
+  OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT,
+  OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION,
+  OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP, SECURE_AUTH,
+  REPORT_DATA_TRUNCATION, OPT_RECONNECT, MYSQL_OPT_NONBLOCK = 6000
+
+ + + +
+
+
+
PBIND = ptr BIND
+
+ + + +
+
+
+
Pcharset_info_st = ptr Charset_info_st
+
+ + + +
+
+
+
PDATA = ptr DATA
+
+ + + +
+
+
+
PFIELD = ptr FIELD
+
+ + + +
+
+
+
Pmy_bool = ptr my_bool
+
+ + + +
+
+
+
PMySQL = ptr MySQL
+
+ + + +
+
+
+
PNET = ptr NET
+
+ + + +
+
+
+
PPByte = pointer
+
+ + + +
+
+
+
PRES = ptr RES
+
+ + + +
+
+
+
PROWS = ptr ROWS
+
+ + + +
+
+
+
Pst_dynamic_array = ptr St_dynamic_array
+
+ + + +
+
+
+
Pst_mysql = ptr St_mysql
+
+ + + +
+
+
+
Pst_mysql_methods = ptr St_mysql_methods
+
+ + + +
+
+
+
Pst_mysql_rows = ptr St_mysql_rows
+
+ + + +
+
+
+
Pst_mysql_stmt = ptr St_mysql_stmt
+
+ + + +
+
+
+
Pst_used_mem = ptr St_used_mem
+
+ + + +
+
+
+
PSTMT = ptr STMT
+
+ + + +
+
+
+
PUSED_MEM = ptr USED_MEM
+
+ + + +
+
+
+
PVIO = pointer
+
+ + + +
+
+
+
RES = St_mysql_res
+
+ + + +
+
+
+
ROW = cstringArray
+
+ + + +
+
+
+
ROWS = St_mysql_rows
+
+ + + +
+
+
+
St_dynamic_array {.final.} = object
+  buffer*: cstring
+  elements*: cuint
+  max_element*: cuint
+  alloc_increment*: cuint
+  size_of_element*: cuint
+
+ + + +
+
+
+
St_mem_root {.final.} = object
+  free*: PUSED_MEM
+  used*: PUSED_MEM
+  pre_alloc*: PUSED_MEM
+  min_malloc*: cuint
+  block_size*: cuint
+  block_num*: cuint
+  first_block_usage*: cuint
+  error_handler*: proc () {.cdecl.}
+
+ + + +
+
+
+
St_mysql {.final.} = object
+  net*: NET
+  connector_fd*: gptr
+  host*: cstring
+  user*: cstring
+  passwd*: cstring
+  unix_socket*: cstring
+  server_version*: cstring
+  host_info*: cstring
+  info*: cstring
+  db*: cstring
+  charset*: Pcharset_info_st
+  fields*: PFIELD
+  field_alloc*: MEM_ROOT
+  affected_rows*: my_ulonglong
+  insert_id*: my_ulonglong
+  extra_info*: my_ulonglong
+  thread_id*: int
+  packet_length*: int
+  port*: cuint
+  client_flag*: int
+  server_capabilities*: int
+  protocol_version*: cuint
+  field_count*: cuint
+  server_status*: cuint
+  server_language*: cuint
+  warning_count*: cuint
+  options*: St_mysql_options
+  status*: Status
+  free_me*: my_bool
+  reconnect*: my_bool
+  scramble*: array[0 .. 21 - 1, char]
+  rpl_pivot*: my_bool
+  master*: Pst_mysql
+  next_slave*: Pst_mysql
+  last_used_slave*: Pst_mysql
+  last_used_con*: Pst_mysql
+  stmts*: pointer
+  methods*: Pst_mysql_methods
+  thd*: pointer
+  unbuffered_fetch_owner*: Pmy_bool
+
+ + + +
+
+
+
St_mysql_bind {.final.} = object
+  length*: ptr culong
+  is_null*: Pmy_bool
+  buffer*: pointer
+  error*: Pmy_bool
+  u*: U
+  store_param_func*: proc (net: PNET; param: ptr St_mysql_bind) {.cdecl.}
+  fetch_result*: proc (param: ptr St_mysql_bind; field: PFIELD; row: PPByte) {.
+      cdecl.}
+  skip_result*: proc (param: ptr St_mysql_bind; field: PFIELD; row: PPByte) {.
+      cdecl.}
+  buffer_length*: culong
+  offset*: culong
+  length_value*: culong
+  flags*: cuint
+  pack_length*: cuint
+  buffer_type*: Enum_field_types
+  error_value*: my_bool
+  is_unsigned*: my_bool
+  long_data_used*: my_bool
+  is_null_value*: my_bool
+  extension*: pointer
+
+ + + +
+
+
+
St_mysql_data {.final.} = object
+  rows*: my_ulonglong
+  fields*: cuint
+  data*: PROWS
+  alloc*: MEM_ROOT
+  prev_ptr*: ptr PROWS
+
+ + + +
+
+
+
St_mysql_field {.final.} = object
+  name*: cstring
+  org_name*: cstring
+  table*: cstring
+  org_table*: cstring
+  db*: cstring
+  catalog*: cstring
+  def*: cstring
+  len*: int
+  max_length*: int
+  name_length*: cuint
+  org_name_length*: cuint
+  table_length*: cuint
+  org_table_length*: cuint
+  db_length*: cuint
+  catalog_length*: cuint
+  def_length*: cuint
+  flags*: cuint
+  decimals*: cuint
+  charsetnr*: cuint
+  ftype*: Enum_field_types
+  extension*: pointer
+
+ + + +
+
+
+
St_mysql_methods {.final.} = object
+  read_query_result*: proc (MySQL: PMySQL): my_bool {.cdecl.}
+  advanced_command*: proc (MySQL: PMySQL; command: Enum_server_command;
+                           header: cstring; header_length: int; arg: cstring;
+                           arg_length: int; skip_check: my_bool): my_bool
+  read_rows*: proc (MySQL: PMySQL; fields: PFIELD; fields_count: cuint): PDATA
+  use_result*: proc (MySQL: PMySQL): PRES
+  fetch_lengths*: proc (fto: ptr int; column: ROW; field_count: cuint)
+  flush_use_result*: proc (MySQL: PMySQL)
+  list_fields*: proc (MySQL: PMySQL): PFIELD
+  read_prepare_result*: proc (MySQL: PMySQL; stmt: PSTMT): my_bool
+  stmt_execute*: proc (stmt: PSTMT): cint
+  read_binary_rows*: proc (stmt: PSTMT): cint
+  unbuffered_fetch*: proc (MySQL: PMySQL; row: cstringArray): cint
+  free_embedded_thd*: proc (MySQL: PMySQL)
+  read_statistics*: proc (MySQL: PMySQL): cstring
+  next_result*: proc (MySQL: PMySQL): my_bool
+  read_change_user_result*: proc (MySQL: PMySQL; buff: cstring; passwd: cstring): cint
+  read_rowsfrom_cursor*: proc (stmt: PSTMT): cint
+
+ + + +
+
+
+
St_mysql_options {.final.} = object
+  connect_timeout*: cuint
+  read_timeout*: cuint
+  write_timeout*: cuint
+  port*: cuint
+  protocol*: cuint
+  client_flag*: int
+  host*: cstring
+  user*: cstring
+  password*: cstring
+  unix_socket*: cstring
+  db*: cstring
+  init_commands*: Pst_dynamic_array
+  my_cnf_file*: cstring
+  my_cnf_group*: cstring
+  charset_dir*: cstring
+  charset_name*: cstring
+  ssl_key*: cstring
+  ssl_cert*: cstring
+  ssl_ca*: cstring
+  ssl_capath*: cstring
+  ssl_cipher*: cstring
+  shared_memory_base_name*: cstring
+  max_allowed_packet*: int
+  use_ssl*: my_bool
+  compress*: my_bool
+  named_pipe*: my_bool
+  rpl_probe*: my_bool
+  rpl_parse*: my_bool
+  no_master_reads*: my_bool
+  separate_thread*: my_bool
+  methods_to_use*: Option
+  client_ip*: cstring
+  secure_auth*: my_bool
+  report_data_truncation*: my_bool
+  local_infile_init*: proc (para1: var pointer; para2: cstring; para3: pointer): cint {.
+      cdecl.}
+  local_infile_read*: proc (para1: pointer; para2: cstring; para3: cuint): cint
+  local_infile_end*: proc (para1: pointer)
+  local_infile_error*: proc (para1: pointer; para2: cstring; para3: cuint): cint
+  local_infile_userdata*: pointer
+
+ + + +
+
+
+
St_mysql_res {.final.} = object
+  row_count*: my_ulonglong
+  fields*: PFIELD
+  data*: PDATA
+  data_cursor*: PROWS
+  lengths*: ptr int
+  handle*: PMySQL
+  field_alloc*: MEM_ROOT
+  field_count*: cuint
+  current_field*: cuint
+  row*: ROW
+  current_row*: ROW
+  eof*: my_bool
+  unbuffered_fetch_cancelled*: my_bool
+  methods*: Pst_mysql_methods
+
+ + + +
+
+
+
St_mysql_rows {.final.} = object
+  next*: Pst_mysql_rows
+  data*: ROW
+  len*: int
+
+ + + +
+
+
+
St_mysql_stmt {.final.} = object
+  mem_root*: MEM_ROOT
+  mysql*: PMySQL
+  params*: PBIND
+  fields*: PFIELD
+  result*: DATA
+  data_cursor*: PROWS
+  affected_rows*: my_ulonglong
+  insert_id*: my_ulonglong
+  read_row_func*: proc (stmt: Pst_mysql_stmt; row: PPByte): cint {.cdecl.}
+  stmt_id*: int
+  flags*: int
+  prefetch_rows*: int
+  server_status*: cuint
+  last_errno*: cuint
+  param_count*: cuint
+  field_count*: cuint
+  state*: Enum_mysql_stmt_state
+  last_error*: array[0 .. 200 - 1, char]
+  sqlstate*: array[0 .. 6 - 1, char]
+  send_types_to_server*: my_bool
+  bind_param_done*: my_bool
+  bind_result_done*: char
+  unbuffered_fetch_cancelled*: my_bool
+  update_max_length*: my_bool
+
+ + + +
+
+
+
St_net {.final.} = object
+  vio*: PVIO
+  buff*: cstring
+  buff_end*: cstring
+  write_pos*: cstring
+  read_pos*: cstring
+  fd*: my_socket
+  max_packet*: int
+  max_packet_size*: int
+  pkt_nr*: cuint
+  compress_pkt_nr*: cuint
+  write_timeout*: cuint
+  read_timeout*: cuint
+  retry_count*: cuint
+  fcntl*: cint
+  compress*: my_bool
+  remain_in_buf*: int
+  len*: int
+  buf_length*: int
+  where_b*: int
+  return_status*: ptr cint
+  reading_or_writing*: char
+  save_char*: cchar
+  no_send_ok*: my_bool
+  no_send_eof*: my_bool
+  no_send_error*: my_bool
+  last_error*: array[0 .. 200 - 1, char]
+  sqlstate*: array[0 .. 6 - 1, char]
+  last_errno*: cuint
+  error*: char
+  query_cache_query*: gptr
+  report_error*: my_bool
+  return_errno*: my_bool
+
+ + + +
+
+
+
St_used_mem {.final.} = object
+  next*: Pst_used_mem
+  left*: cuint
+  size*: cuint
+
+ + + +
+
+
+
Status = enum
+  STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT
+
+ + + +
+
+
+
STMT = St_mysql_stmt
+
+ + + +
+
+
+
U {.final, union, pure.} = object
+
+ + + +
+
+
+
USED_MEM = St_used_mem
+
+ + + +
+
+ +
+
+
+

Consts

+
+
+
ERRMSG_SIZE = 200
+
+ + + +
+
+
+
MYSQL_WAIT_EXCEPT = 4
+
+ + + +
+
+
+
MYSQL_WAIT_READ = 1
+
+ + + +
+
+
+
MYSQL_WAIT_TIMEOUT = 8
+
+ + + +
+
+
+
MYSQL_WAIT_WRITE = 2
+
+ + + +
+
+
+
NOT_NULL_FLAG = 1
+
+ + + +
+
+
+
PRI_KEY_FLAG = 2
+
+ + + +
+
+
+
SCRAMBLE_LENGTH = 20
+
+ + + +
+
+
+
SQLSTATE_LENGTH = 5
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc close(sock: PMySQL) {.stdcall, dynlib: lib, importc: "mysql_close",
+                           ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc error(MySQL: PMySQL): cstring {.stdcall, dynlib: lib,
+                                     importc: "mysql_error", ...raises: [],
+                                     tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetch_field_direct(res: PRES; fieldnr: cuint): PFIELD {.stdcall,
+    dynlib: lib, importc: "mysql_fetch_field_direct", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetch_row(result: PRES): ROW {.stdcall, dynlib: lib,
+                                    importc: "mysql_fetch_row", ...raises: [],
+                                    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetch_row_cont(ret: ptr ROW; result: PRES; status: cint): cint {.stdcall,
+    dynlib: lib, importc: "mysql_fetch_row_cont", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetch_row_start(ret: ptr ROW; result: PRES): cint {.stdcall, dynlib: lib,
+    importc: "mysql_fetch_row_start", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc free_result(result: PRES) {.stdcall, dynlib: lib,
+                                 importc: "mysql_free_result", ...raises: [],
+                                 tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc free_result_cont(result: PRES; status: cint): cint {.stdcall, dynlib: lib,
+    importc: "mysql_free_result_cont", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc free_result_start(result: PRES): cint {.stdcall, dynlib: lib,
+    importc: "mysql_free_result_start", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc get_socket(MySQL: PMySQL): my_socket {.stdcall, dynlib: lib,
+    importc: "mysql_get_socket", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc get_timeout_value(MySQL: PMySQL): cuint {.stdcall, dynlib: lib,
+    importc: "mysql_get_timeout_value", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc get_timeout_value_ms(MySQL: PMySQL): cuint {.stdcall, dynlib: lib,
+    importc: "mysql_get_timeout_value_ms", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc init(MySQL: PMySQL): PMySQL {.stdcall, dynlib: lib, importc: "mysql_init",
+                                   ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc init(MySQL: var PMySQL) {.stdcall, dynlib: lib, importc: "mysql_init",
+                               ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc num_fields(res: PRES): cuint {.stdcall, dynlib: lib,
+                                    importc: "mysql_num_fields", ...raises: [],
+                                    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc options(MySQL: PMySQL; option: Option; arg: pointer): cint {.stdcall,
+    dynlib: lib, importc: "mysql_options", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc optionsv(MySQL: PMySQL; option: Option): cint {.cdecl, dynlib: lib,
+    importc: "mysql_optionsv", varargs, ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc ping(MySQL: PMySQL): cint {.stdcall, dynlib: lib, importc: "mysql_ping",
+                                 ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc read_query_result(MySQL: PMySQL): my_bool {.stdcall, dynlib: lib,
+    importc: "mysql_read_query_result", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc read_query_result_cont(ret: ptr my_bool; MySQL: PMySQL; status: cint): cint {.
+    stdcall, dynlib: lib, importc: "mysql_read_query_result_cont", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc read_query_result_start(ret: ptr my_bool; MySQL: PMySQL): cint {.stdcall,
+    dynlib: lib, importc: "mysql_read_query_result_start", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_connect(MySQL: PMySQL; host: cstring; user: cstring; passwd: cstring;
+                  db: cstring; port: cuint; unix_socket: cstring;
+                  clientflag: int): PMySQL {.stdcall, dynlib: lib,
+    importc: "mysql_real_connect", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_connect_cont(ret: ptr PMySQL; MySQL: PMySQL; status: cint): cint {.
+    stdcall, dynlib: lib, importc: "mysql_real_connect_cont", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_connect_start(ret: ptr PMySQL; MySQL: PMySQL; host: cstring;
+                        user: cstring; passwd: cstring; db: cstring;
+                        port: cuint; unix_socket: cstring; clientflag: culong): cint {.
+    stdcall, dynlib: lib, importc: "mysql_real_connect_start", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_escape_string(MySQL: PMySQL; fto: cstring; from: cstring; len: int): int {.
+    stdcall, dynlib: lib, importc: "mysql_real_escape_string", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_query(MySQL: PMySQL; q: cstring; len: int): cint {.stdcall,
+    dynlib: lib, importc: "mysql_real_query", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_query_cont(ret: ptr cint; MySQL: PMySQL; status: cint): cint {.
+    stdcall, dynlib: lib, importc: "mysql_real_query_cont", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_query_start(ret: ptr cint; MySQL: PMySQL; q: cstring; len: culong): cint {.
+    stdcall, dynlib: lib, importc: "mysql_real_query_start", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc stmt_init(MySQL: PMySQL): PSTMT {.stdcall, dynlib: lib,
+                                       importc: "mysql_stmt_init", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc store_result_cont(ret: ptr PRES; MySQL: PMySQL; status: cint): cint {.
+    stdcall, dynlib: lib, importc: "mysql_store_result_cont", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc store_result_start(ret: ptr PRES; MySQL: PMySQL): cint {.stdcall,
+    dynlib: lib, importc: "mysql_store_result_start", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc use_result(MySQL: PMySQL): PRES {.stdcall, dynlib: lib,
+                                       importc: "mysql_use_result", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.idx b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.idx new file mode 100644 index 0000000..4680479 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mariadb/mariadb_rdb.idx @@ -0,0 +1,156 @@ +nimTitle mariadb_rdb query_builder/libs/mariadb/mariadb_rdb.html module src/allographer/query_builder/libs/mariadb/mariadb_rdb 0 +nim false query_builder/libs/mariadb/mariadb_rdb.html#false my_bool.false 21 +nim true query_builder/libs/mariadb/mariadb_rdb.html#true my_bool.true 21 +nim my_bool query_builder/libs/mariadb/mariadb_rdb.html#my_bool type my_bool 21 +nim Pmy_bool query_builder/libs/mariadb/mariadb_rdb.html#Pmy_bool type Pmy_bool 22 +nim PVIO query_builder/libs/mariadb/mariadb_rdb.html#PVIO type PVIO 23 +nim gptr query_builder/libs/mariadb/mariadb_rdb.html#gptr type gptr 25 +nim my_socket query_builder/libs/mariadb/mariadb_rdb.html#my_socket type my_socket 27 +nim PPByte query_builder/libs/mariadb/mariadb_rdb.html#PPByte type PPByte 28 +nim cuint query_builder/libs/mariadb/mariadb_rdb.html#cuint type cuint 29 +nim MYSQL_WAIT_READ query_builder/libs/mariadb/mariadb_rdb.html#MYSQL_WAIT_READ const MYSQL_WAIT_READ 44 +nim MYSQL_WAIT_WRITE query_builder/libs/mariadb/mariadb_rdb.html#MYSQL_WAIT_WRITE const MYSQL_WAIT_WRITE 45 +nim MYSQL_WAIT_EXCEPT query_builder/libs/mariadb/mariadb_rdb.html#MYSQL_WAIT_EXCEPT const MYSQL_WAIT_EXCEPT 46 +nim MYSQL_WAIT_TIMEOUT query_builder/libs/mariadb/mariadb_rdb.html#MYSQL_WAIT_TIMEOUT const MYSQL_WAIT_TIMEOUT 47 +nim SQLSTATE_LENGTH query_builder/libs/mariadb/mariadb_rdb.html#SQLSTATE_LENGTH const SQLSTATE_LENGTH 52 +nim Enum_server_command query_builder/libs/mariadb/mariadb_rdb.html#Enum_server_command enum Enum_server_command 61 +nim SCRAMBLE_LENGTH query_builder/libs/mariadb/mariadb_rdb.html#SCRAMBLE_LENGTH const SCRAMBLE_LENGTH 72 +nim NOT_NULL_FLAG query_builder/libs/mariadb/mariadb_rdb.html#NOT_NULL_FLAG const NOT_NULL_FLAG 79 +nim PRI_KEY_FLAG query_builder/libs/mariadb/mariadb_rdb.html#PRI_KEY_FLAG const PRI_KEY_FLAG 80 +nim ERRMSG_SIZE query_builder/libs/mariadb/mariadb_rdb.html#ERRMSG_SIZE const ERRMSG_SIZE 145 +nim St_net query_builder/libs/mariadb/mariadb_rdb.html#St_net object St_net 162 +nim NET query_builder/libs/mariadb/mariadb_rdb.html#NET type NET 202 +nim PNET query_builder/libs/mariadb/mariadb_rdb.html#PNET type PNET 203 +nim TYPE_DECIMAL query_builder/libs/mariadb/mariadb_rdb.html#TYPE_DECIMAL Enum_field_types.TYPE_DECIMAL 210 +nim TYPE_TINY query_builder/libs/mariadb/mariadb_rdb.html#TYPE_TINY Enum_field_types.TYPE_TINY 210 +nim TYPE_SHORT query_builder/libs/mariadb/mariadb_rdb.html#TYPE_SHORT Enum_field_types.TYPE_SHORT 210 +nim TYPE_LONG query_builder/libs/mariadb/mariadb_rdb.html#TYPE_LONG Enum_field_types.TYPE_LONG 210 +nim TYPE_FLOAT query_builder/libs/mariadb/mariadb_rdb.html#TYPE_FLOAT Enum_field_types.TYPE_FLOAT 210 +nim TYPE_DOUBLE query_builder/libs/mariadb/mariadb_rdb.html#TYPE_DOUBLE Enum_field_types.TYPE_DOUBLE 210 +nim TYPE_NULL query_builder/libs/mariadb/mariadb_rdb.html#TYPE_NULL Enum_field_types.TYPE_NULL 210 +nim TYPE_TIMESTAMP query_builder/libs/mariadb/mariadb_rdb.html#TYPE_TIMESTAMP Enum_field_types.TYPE_TIMESTAMP 210 +nim TYPE_LONGLONG query_builder/libs/mariadb/mariadb_rdb.html#TYPE_LONGLONG Enum_field_types.TYPE_LONGLONG 210 +nim TYPE_INT24 query_builder/libs/mariadb/mariadb_rdb.html#TYPE_INT24 Enum_field_types.TYPE_INT24 210 +nim TYPE_DATE query_builder/libs/mariadb/mariadb_rdb.html#TYPE_DATE Enum_field_types.TYPE_DATE 210 +nim TYPE_TIME query_builder/libs/mariadb/mariadb_rdb.html#TYPE_TIME Enum_field_types.TYPE_TIME 210 +nim TYPE_DATETIME query_builder/libs/mariadb/mariadb_rdb.html#TYPE_DATETIME Enum_field_types.TYPE_DATETIME 210 +nim TYPE_YEAR query_builder/libs/mariadb/mariadb_rdb.html#TYPE_YEAR Enum_field_types.TYPE_YEAR 210 +nim TYPE_NEWDATE query_builder/libs/mariadb/mariadb_rdb.html#TYPE_NEWDATE Enum_field_types.TYPE_NEWDATE 210 +nim TYPE_VARCHAR query_builder/libs/mariadb/mariadb_rdb.html#TYPE_VARCHAR Enum_field_types.TYPE_VARCHAR 210 +nim TYPE_BIT query_builder/libs/mariadb/mariadb_rdb.html#TYPE_BIT Enum_field_types.TYPE_BIT 210 +nim TYPE_NEWDECIMAL query_builder/libs/mariadb/mariadb_rdb.html#TYPE_NEWDECIMAL Enum_field_types.TYPE_NEWDECIMAL 210 +nim TYPE_ENUM query_builder/libs/mariadb/mariadb_rdb.html#TYPE_ENUM Enum_field_types.TYPE_ENUM 210 +nim TYPE_SET query_builder/libs/mariadb/mariadb_rdb.html#TYPE_SET Enum_field_types.TYPE_SET 210 +nim TYPE_TINY_BLOB query_builder/libs/mariadb/mariadb_rdb.html#TYPE_TINY_BLOB Enum_field_types.TYPE_TINY_BLOB 210 +nim TYPE_MEDIUM_BLOB query_builder/libs/mariadb/mariadb_rdb.html#TYPE_MEDIUM_BLOB Enum_field_types.TYPE_MEDIUM_BLOB 210 +nim TYPE_LONG_BLOB query_builder/libs/mariadb/mariadb_rdb.html#TYPE_LONG_BLOB Enum_field_types.TYPE_LONG_BLOB 210 +nim TYPE_BLOB query_builder/libs/mariadb/mariadb_rdb.html#TYPE_BLOB Enum_field_types.TYPE_BLOB 210 +nim TYPE_VAR_STRING query_builder/libs/mariadb/mariadb_rdb.html#TYPE_VAR_STRING Enum_field_types.TYPE_VAR_STRING 210 +nim TYPE_STRING query_builder/libs/mariadb/mariadb_rdb.html#TYPE_STRING Enum_field_types.TYPE_STRING 210 +nim TYPE_GEOMETRY query_builder/libs/mariadb/mariadb_rdb.html#TYPE_GEOMETRY Enum_field_types.TYPE_GEOMETRY 210 +nim Enum_field_types query_builder/libs/mariadb/mariadb_rdb.html#Enum_field_types enum Enum_field_types 210 +nim St_mysql_field query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_field object St_mysql_field 407 +nim FIELD query_builder/libs/mariadb/mariadb_rdb.html#FIELD type FIELD 430 +nim PFIELD query_builder/libs/mariadb/mariadb_rdb.html#PFIELD type PFIELD 431 +nim ROW query_builder/libs/mariadb/mariadb_rdb.html#ROW type ROW 433 +nim my_ulonglong query_builder/libs/mariadb/mariadb_rdb.html#my_ulonglong type my_ulonglong 446 +nim Pst_mysql_rows query_builder/libs/mariadb/mariadb_rdb.html#Pst_mysql_rows type Pst_mysql_rows 453 +nim St_mysql_rows query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_rows object St_mysql_rows 454 +nim ROWS query_builder/libs/mariadb/mariadb_rdb.html#ROWS type ROWS 459 +nim PROWS query_builder/libs/mariadb/mariadb_rdb.html#PROWS type PROWS 460 +nim Pst_used_mem query_builder/libs/mariadb/mariadb_rdb.html#Pst_used_mem type Pst_used_mem 471 +nim St_used_mem query_builder/libs/mariadb/mariadb_rdb.html#St_used_mem object St_used_mem 472 +nim USED_MEM query_builder/libs/mariadb/mariadb_rdb.html#USED_MEM type USED_MEM 477 +nim PUSED_MEM query_builder/libs/mariadb/mariadb_rdb.html#PUSED_MEM type PUSED_MEM 478 +nim St_mem_root query_builder/libs/mariadb/mariadb_rdb.html#St_mem_root object St_mem_root 480 +nim MEM_ROOT query_builder/libs/mariadb/mariadb_rdb.html#MEM_ROOT type MEM_ROOT 492 +nim St_mysql_data query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_data object St_mysql_data 499 +nim DATA query_builder/libs/mariadb/mariadb_rdb.html#DATA type DATA 506 +nim PDATA query_builder/libs/mariadb/mariadb_rdb.html#PDATA type PDATA 507 +nim OPT_CONNECT_TIMEOUT query_builder/libs/mariadb/mariadb_rdb.html#OPT_CONNECT_TIMEOUT Option.OPT_CONNECT_TIMEOUT 508 +nim OPT_COMPRESS query_builder/libs/mariadb/mariadb_rdb.html#OPT_COMPRESS Option.OPT_COMPRESS 508 +nim OPT_NAMED_PIPE query_builder/libs/mariadb/mariadb_rdb.html#OPT_NAMED_PIPE Option.OPT_NAMED_PIPE 508 +nim INIT_COMMAND query_builder/libs/mariadb/mariadb_rdb.html#INIT_COMMAND Option.INIT_COMMAND 508 +nim READ_DEFAULT_FILE query_builder/libs/mariadb/mariadb_rdb.html#READ_DEFAULT_FILE Option.READ_DEFAULT_FILE 508 +nim READ_DEFAULT_GROUP query_builder/libs/mariadb/mariadb_rdb.html#READ_DEFAULT_GROUP Option.READ_DEFAULT_GROUP 508 +nim SET_CHARSET_DIR query_builder/libs/mariadb/mariadb_rdb.html#SET_CHARSET_DIR Option.SET_CHARSET_DIR 508 +nim SET_CHARSET_NAME query_builder/libs/mariadb/mariadb_rdb.html#SET_CHARSET_NAME Option.SET_CHARSET_NAME 508 +nim OPT_LOCAL_INFILE query_builder/libs/mariadb/mariadb_rdb.html#OPT_LOCAL_INFILE Option.OPT_LOCAL_INFILE 508 +nim OPT_PROTOCOL query_builder/libs/mariadb/mariadb_rdb.html#OPT_PROTOCOL Option.OPT_PROTOCOL 508 +nim SHARED_MEMORY_BASE_NAME query_builder/libs/mariadb/mariadb_rdb.html#SHARED_MEMORY_BASE_NAME Option.SHARED_MEMORY_BASE_NAME 508 +nim OPT_READ_TIMEOUT query_builder/libs/mariadb/mariadb_rdb.html#OPT_READ_TIMEOUT Option.OPT_READ_TIMEOUT 508 +nim OPT_WRITE_TIMEOUT query_builder/libs/mariadb/mariadb_rdb.html#OPT_WRITE_TIMEOUT Option.OPT_WRITE_TIMEOUT 508 +nim OPT_USE_RESULT query_builder/libs/mariadb/mariadb_rdb.html#OPT_USE_RESULT Option.OPT_USE_RESULT 508 +nim OPT_USE_REMOTE_CONNECTION query_builder/libs/mariadb/mariadb_rdb.html#OPT_USE_REMOTE_CONNECTION Option.OPT_USE_REMOTE_CONNECTION 508 +nim OPT_USE_EMBEDDED_CONNECTION query_builder/libs/mariadb/mariadb_rdb.html#OPT_USE_EMBEDDED_CONNECTION Option.OPT_USE_EMBEDDED_CONNECTION 508 +nim OPT_GUESS_CONNECTION query_builder/libs/mariadb/mariadb_rdb.html#OPT_GUESS_CONNECTION Option.OPT_GUESS_CONNECTION 508 +nim SET_CLIENT_IP query_builder/libs/mariadb/mariadb_rdb.html#SET_CLIENT_IP Option.SET_CLIENT_IP 508 +nim SECURE_AUTH query_builder/libs/mariadb/mariadb_rdb.html#SECURE_AUTH Option.SECURE_AUTH 508 +nim REPORT_DATA_TRUNCATION query_builder/libs/mariadb/mariadb_rdb.html#REPORT_DATA_TRUNCATION Option.REPORT_DATA_TRUNCATION 508 +nim OPT_RECONNECT query_builder/libs/mariadb/mariadb_rdb.html#OPT_RECONNECT Option.OPT_RECONNECT 508 +nim MYSQL_OPT_NONBLOCK query_builder/libs/mariadb/mariadb_rdb.html#MYSQL_OPT_NONBLOCK Option.MYSQL_OPT_NONBLOCK 508 +nim Option query_builder/libs/mariadb/mariadb_rdb.html#Option enum Option 508 +nim St_dynamic_array query_builder/libs/mariadb/mariadb_rdb.html#St_dynamic_array object St_dynamic_array 528 +nim Pst_dynamic_array query_builder/libs/mariadb/mariadb_rdb.html#Pst_dynamic_array type Pst_dynamic_array 536 +nim St_mysql_options query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_options object St_mysql_options 538 +nim STATUS_READY query_builder/libs/mariadb/mariadb_rdb.html#STATUS_READY Status.STATUS_READY 584 +nim STATUS_GET_RESULT query_builder/libs/mariadb/mariadb_rdb.html#STATUS_GET_RESULT Status.STATUS_GET_RESULT 584 +nim STATUS_USE_RESULT query_builder/libs/mariadb/mariadb_rdb.html#STATUS_USE_RESULT Status.STATUS_USE_RESULT 584 +nim Status query_builder/libs/mariadb/mariadb_rdb.html#Status enum Status 584 +nim Charset_info_st query_builder/libs/mariadb/mariadb_rdb.html#Charset_info_st object Charset_info_st 593 +nim Pcharset_info_st query_builder/libs/mariadb/mariadb_rdb.html#Pcharset_info_st type Pcharset_info_st 622 +nim Pst_mysql_methods query_builder/libs/mariadb/mariadb_rdb.html#Pst_mysql_methods type Pst_mysql_methods 636 +nim Pst_mysql query_builder/libs/mariadb/mariadb_rdb.html#Pst_mysql type Pst_mysql 637 +nim St_mysql query_builder/libs/mariadb/mariadb_rdb.html#St_mysql object St_mysql 638 +nim MySQL query_builder/libs/mariadb/mariadb_rdb.html#MySQL type MySQL 684 +nim PMySQL query_builder/libs/mariadb/mariadb_rdb.html#PMySQL type PMySQL 685 +nim St_mysql_res query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_res object St_mysql_res 687 +nim RES query_builder/libs/mariadb/mariadb_rdb.html#RES type RES 703 +nim PRES query_builder/libs/mariadb/mariadb_rdb.html#PRES type PRES 704 +nim Pst_mysql_stmt query_builder/libs/mariadb/mariadb_rdb.html#Pst_mysql_stmt type Pst_mysql_stmt 705 +nim PSTMT query_builder/libs/mariadb/mariadb_rdb.html#PSTMT type PSTMT 706 +nim St_mysql_methods query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_methods object St_mysql_methods 707 +nim STMT_INIT_DONE query_builder/libs/mariadb/mariadb_rdb.html#STMT_INIT_DONE Enum_mysql_stmt_state.STMT_INIT_DONE 755 +nim STMT_PREPARE_DONE query_builder/libs/mariadb/mariadb_rdb.html#STMT_PREPARE_DONE Enum_mysql_stmt_state.STMT_PREPARE_DONE 755 +nim STMT_EXECUTE_DONE query_builder/libs/mariadb/mariadb_rdb.html#STMT_EXECUTE_DONE Enum_mysql_stmt_state.STMT_EXECUTE_DONE 755 +nim STMT_FETCH_DONE query_builder/libs/mariadb/mariadb_rdb.html#STMT_FETCH_DONE Enum_mysql_stmt_state.STMT_FETCH_DONE 755 +nim Enum_mysql_stmt_state query_builder/libs/mariadb/mariadb_rdb.html#Enum_mysql_stmt_state enum Enum_mysql_stmt_state 755 +nim U query_builder/libs/mariadb/mariadb_rdb.html#U object U 779 +nim St_mysql_bind query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_bind object St_mysql_bind 783 +nim BIND query_builder/libs/mariadb/mariadb_rdb.html#BIND type BIND 804 +nim PBIND query_builder/libs/mariadb/mariadb_rdb.html#PBIND type PBIND 805 +nim St_mysql_stmt query_builder/libs/mariadb/mariadb_rdb.html#St_mysql_stmt object St_mysql_stmt 806 +nim STMT query_builder/libs/mariadb/mariadb_rdb.html#STMT type STMT 834 +nim num_fields query_builder/libs/mariadb/mariadb_rdb.html#num_fields,PRES proc num_fields(res: PRES): cuint 877 +nim fetch_field_direct query_builder/libs/mariadb/mariadb_rdb.html#fetch_field_direct,PRES,cuint proc fetch_field_direct(res: PRES; fieldnr: cuint): PFIELD 880 +nim error query_builder/libs/mariadb/mariadb_rdb.html#error,PMySQL proc error(MySQL: PMySQL): cstring 895 +nim init query_builder/libs/mariadb/mariadb_rdb.html#init,PMySQL proc init(MySQL: PMySQL): PMySQL 905 +nim init query_builder/libs/mariadb/mariadb_rdb.html#init,PMySQL_2 proc init(MySQL: var PMySQL) 906 +nim real_connect query_builder/libs/mariadb/mariadb_rdb.html#real_connect,PMySQL,cstring,cstring,cstring,cstring,cuint,cstring,int proc real_connect(MySQL: PMySQL; host: cstring; user: cstring; passwd: cstring;\n db: cstring; port: cuint; unix_socket: cstring; clientflag: int): PMySQL 912 +nim real_query query_builder/libs/mariadb/mariadb_rdb.html#real_query,PMySQL,cstring,int proc real_query(MySQL: PMySQL; q: cstring; len: int): cint 935 +nim use_result query_builder/libs/mariadb/mariadb_rdb.html#use_result,PMySQL proc use_result(MySQL: PMySQL): PRES 939 +nim ping query_builder/libs/mariadb/mariadb_rdb.html#ping,PMySQL proc ping(MySQL: PMySQL): cint 995 +nim options query_builder/libs/mariadb/mariadb_rdb.html#options,PMySQL,Option,pointer proc options(MySQL: PMySQL; option: Option; arg: pointer): cint 1017 +nim optionsv query_builder/libs/mariadb/mariadb_rdb.html#optionsv,PMySQL,Option proc optionsv(MySQL: PMySQL; option: Option): cint 1019 +nim get_socket query_builder/libs/mariadb/mariadb_rdb.html#get_socket,PMySQL proc get_socket(MySQL: PMySQL): my_socket 1021 +nim get_timeout_value query_builder/libs/mariadb/mariadb_rdb.html#get_timeout_value,PMySQL proc get_timeout_value(MySQL: PMySQL): cuint 1023 +nim get_timeout_value_ms query_builder/libs/mariadb/mariadb_rdb.html#get_timeout_value_ms,PMySQL proc get_timeout_value_ms(MySQL: PMySQL): cuint 1025 +nim free_result query_builder/libs/mariadb/mariadb_rdb.html#free_result,PRES proc free_result(result: PRES) 1027 +nim free_result_start query_builder/libs/mariadb/mariadb_rdb.html#free_result_start,PRES proc free_result_start(result: PRES): cint 1029 +nim free_result_cont query_builder/libs/mariadb/mariadb_rdb.html#free_result_cont,PRES,cint proc free_result_cont(result: PRES; status: cint): cint 1031 +nim fetch_row query_builder/libs/mariadb/mariadb_rdb.html#fetch_row,PRES proc fetch_row(result: PRES): ROW 1039 +nim fetch_row_start query_builder/libs/mariadb/mariadb_rdb.html#fetch_row_start,ptr.ROW,PRES proc fetch_row_start(ret: ptr ROW; result: PRES): cint 1041 +nim fetch_row_cont query_builder/libs/mariadb/mariadb_rdb.html#fetch_row_cont,ptr.ROW,PRES,cint proc fetch_row_cont(ret: ptr ROW; result: PRES; status: cint): cint 1043 +nim real_escape_string query_builder/libs/mariadb/mariadb_rdb.html#real_escape_string,PMySQL,cstring,cstring,int proc real_escape_string(MySQL: PMySQL; fto: cstring; from: cstring; len: int): int 1055 +nim read_query_result query_builder/libs/mariadb/mariadb_rdb.html#read_query_result,PMySQL proc read_query_result(MySQL: PMySQL): my_bool 1077 +nim read_query_result_start query_builder/libs/mariadb/mariadb_rdb.html#read_query_result_start,ptr.my_bool,PMySQL proc read_query_result_start(ret: ptr my_bool; MySQL: PMySQL): cint 1079 +nim read_query_result_cont query_builder/libs/mariadb/mariadb_rdb.html#read_query_result_cont,ptr.my_bool,PMySQL,cint proc read_query_result_cont(ret: ptr my_bool; MySQL: PMySQL; status: cint): cint 1081 +nim real_connect_start query_builder/libs/mariadb/mariadb_rdb.html#real_connect_start,ptr.PMySQL,PMySQL,cstring,cstring,cstring,cstring,cuint,cstring,culong proc real_connect_start(ret: ptr PMySQL; MySQL: PMySQL; host: cstring; user: cstring;\n passwd: cstring; db: cstring; port: cuint;\n unix_socket: cstring; clientflag: culong): cint 1083 +nim real_connect_cont query_builder/libs/mariadb/mariadb_rdb.html#real_connect_cont,ptr.PMySQL,PMySQL,cint proc real_connect_cont(ret: ptr PMySQL; MySQL: PMySQL; status: cint): cint 1087 +nim real_query_start query_builder/libs/mariadb/mariadb_rdb.html#real_query_start,ptr.cint,PMySQL,cstring,culong proc real_query_start(ret: ptr cint; MySQL: PMySQL; q: cstring; len: culong): cint 1089 +nim real_query_cont query_builder/libs/mariadb/mariadb_rdb.html#real_query_cont,ptr.cint,PMySQL,cint proc real_query_cont(ret: ptr cint; MySQL: PMySQL; status: cint): cint 1091 +nim store_result_start query_builder/libs/mariadb/mariadb_rdb.html#store_result_start,ptr.PRES,PMySQL proc store_result_start(ret: ptr PRES; MySQL: PMySQL): cint 1093 +nim store_result_cont query_builder/libs/mariadb/mariadb_rdb.html#store_result_cont,ptr.PRES,PMySQL,cint proc store_result_cont(ret: ptr PRES; MySQL: PMySQL; status: cint): cint 1095 +nim stmt_init query_builder/libs/mariadb/mariadb_rdb.html#stmt_init,PMySQL proc stmt_init(MySQL: PMySQL): PSTMT 1097 +nim close query_builder/libs/mariadb/mariadb_rdb.html#close,PMySQL proc close(sock: PMySQL) 1158 +nimgrp init query_builder/libs/mariadb/mariadb_rdb.html#init-procs-all proc 905 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.html b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.html new file mode 100644 index 0000000..766044d --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.html @@ -0,0 +1,294 @@ + + + + + + + +src/allographer/query_builder/libs/mysql/mysql_impl + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/mysql/mysql_impl

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(db: PMySQL; query: string; args: JsonNode; columns: seq[seq[string]];
+          timeout: int): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, KeyError, DbError], tags: [RootEffect], forbids: [].}
+
+ + args is JArray [{"key":"id", "value": 1}, {"key": "name" "value": "alice"}] + +
+
+
+
proc exec(db: PMySQL; query: string; args: JsonNode; timeout: int): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, ValueError, DbError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc exec(db: PMySQL; query: string; args: seq[string]; timeout: int): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, ValueError, DbError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execGetValue(db: PMySQL; query: string; args: JsonNode;
+                  columns: seq[seq[string]]; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, DbError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getColumns(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[
+    seq[string]] {....stackTrace: false, raises: [Exception, ValueError, DbError],
+                   tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getColumnTypes(db: PMySQL; database, table: string; timeout: int): Future[
+    seq[database_types.Row]] {....stackTrace: false,
+                               raises: [Exception, ValueError, DbError],
+                               tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc query(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, DbError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc query(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, DbError], tags: [RootEffect, TimeEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc queryPlain(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[
+    seq[database_types.Row]] {....stackTrace: false,
+                               raises: [Exception, ValueError, DbError],
+                               tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc queryPlain(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[
+    seq[database_types.Row]] {....stackTrace: false,
+                               raises: [Exception, ValueError, DbError],
+                               tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rawQuery(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[
+    (seq[database_types.Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, DbError], tags: [RootEffect, TimeEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.idx b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.idx new file mode 100644 index 0000000..f8cf021 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_impl.idx @@ -0,0 +1,15 @@ +nimTitle mysql_impl query_builder/libs/mysql/mysql_impl.html module src/allographer/query_builder/libs/mysql/mysql_impl 0 +nim query query_builder/libs/mysql/mysql_impl.html#query,PMySQL,string,seq[string],int proc query(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 36 +nim query query_builder/libs/mysql/mysql_impl.html#query,PMySQL,string,JsonNode,int proc query(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 70 +nim queryPlain query_builder/libs/mysql/mysql_impl.html#queryPlain,PMySQL,string,seq[string],int proc queryPlain(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[\n seq[database_types.Row]] 92 +nim queryPlain query_builder/libs/mysql/mysql_impl.html#queryPlain,PMySQL,string,JsonNode,int proc queryPlain(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[\n seq[database_types.Row]] 114 +nim exec query_builder/libs/mysql/mysql_impl.html#exec,PMySQL,string,seq[string],int proc exec(db: PMySQL; query: string; args: seq[string]; timeout: int): owned(\n Future[void]) 136 +nim exec query_builder/libs/mysql/mysql_impl.html#exec,PMySQL,string,JsonNode,int proc exec(db: PMySQL; query: string; args: JsonNode; timeout: int): owned(\n Future[void]) 142 +nim exec query_builder/libs/mysql/mysql_impl.html#exec,PMySQL,string,JsonNode,seq[seq[string]],int proc exec(db: PMySQL; query: string; args: JsonNode; columns: seq[seq[string]];\n timeout: int): owned(Future[void]) 164 +nim execGetValue query_builder/libs/mysql/mysql_impl.html#execGetValue,PMySQL,string,JsonNode,seq[seq[string]],int proc execGetValue(db: PMySQL; query: string; args: JsonNode;\n columns: seq[seq[string]]; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 172 +nim rawQuery query_builder/libs/mysql/mysql_impl.html#rawQuery,PMySQL,string,JsonNode,int proc rawQuery(db: PMySQL; query: string; args: JsonNode; timeout: int): Future[\n (seq[database_types.Row], DbRows)] 207 +nim getColumns query_builder/libs/mysql/mysql_impl.html#getColumns,PMySQL,string,seq[string],int proc getColumns(db: PMySQL; query: string; args: seq[string]; timeout: int): Future[\n seq[string]] 229 +nim getColumnTypes query_builder/libs/mysql/mysql_impl.html#getColumnTypes,PMySQL,string,string,int proc getColumnTypes(db: PMySQL; database, table: string; timeout: int): Future[\n seq[database_types.Row]] 252 +nimgrp queryplain query_builder/libs/mysql/mysql_impl.html#queryPlain-procs-all proc 92 +nimgrp query query_builder/libs/mysql/mysql_impl.html#query-procs-all proc 36 +nimgrp exec query_builder/libs/mysql/mysql_impl.html#exec-procs-all proc 136 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.html b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.html new file mode 100644 index 0000000..38963e4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.html @@ -0,0 +1,207 @@ + + + + + + + +src/allographer/query_builder/libs/mysql/mysql_lib + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/mysql/mysql_lib

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
MysqlParams = seq[MysqlParamValue]
+
+ + + +
+
+
+
MysqlParamValue = object
+  value*: string
+  isBinary*: bool
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc dbError(db: PMySQL) {.noreturn, ...raises: [DbError], tags: [], forbids: [].}
+
+ + raises a DbError exception. + +
+
+ +
+
+
+
proc dbFormat(conn: PMySQL; query: string; args: MysqlParams): string {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc dbFormat(formatstr: string; args: seq[string]): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromObj(_: type MysqlParams; args: JsonNode; columns: seq[seq[string]]): MysqlParams
+
+ + args is JArray [{"key":"id", "value": 1}, {"key": "name" "value": "alice"}] + +
+
+ +
+
+
+
proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.idx b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.idx new file mode 100644 index 0000000..87fb4f5 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_lib.idx @@ -0,0 +1,9 @@ +nimTitle mysql_lib query_builder/libs/mysql/mysql_lib.html module src/allographer/query_builder/libs/mysql/mysql_lib 0 +nim dbError query_builder/libs/mysql/mysql_lib.html#dbError,PMySQL proc dbError(db: PMySQL) 12 +nim setColumnInfo query_builder/libs/mysql/mysql_lib.html#setColumnInfo,DbColumns,PRES,int proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) 80 +nim dbFormat query_builder/libs/mysql/mysql_lib.html#dbFormat,string,seq[string] proc dbFormat(formatstr: string; args: seq[string]): string 120 +nim MysqlParamValue query_builder/libs/mysql/mysql_lib.html#MysqlParamValue object MysqlParamValue 167 +nim MysqlParams query_builder/libs/mysql/mysql_lib.html#MysqlParams type MysqlParams 171 +nim fromObj query_builder/libs/mysql/mysql_lib.html#fromObj,typeMysqlParams,JsonNode,seq[seq[string]] proc fromObj(_: type MysqlParams; args: JsonNode; columns: seq[seq[string]]): MysqlParams 173 +nim dbFormat query_builder/libs/mysql/mysql_lib.html#dbFormat,PMySQL,string,MysqlParams proc dbFormat(conn: PMySQL; query: string; args: MysqlParams): string 207 +nimgrp dbformat query_builder/libs/mysql/mysql_lib.html#dbFormat-procs-all proc 120 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.html b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.html new file mode 100644 index 0000000..a4fc1be --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.html @@ -0,0 +1,1453 @@ + + + + + + + +src/allographer/query_builder/libs/mysql/mysql_rdb + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/mysql/mysql_rdb

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
BIND = St_mysql_bind
+
+ + + +
+
+
+
Charset_info_st {.final.} = object
+  number*: cuint
+  primary_number*: cuint
+  binary_number*: cuint
+  state*: cuint
+  csname*: cstring
+  name*: cstring
+  comment*: cstring
+  tailoring*: cstring
+  ftype*: cstring
+  to_lower*: cstring
+  to_upper*: cstring
+  sort_order*: cstring
+  contractions*: ptr int16
+  sort_order_big*: ptr ptr int16
+  tab_to_uni*: ptr int16
+  tab_from_uni*: pointer
+  state_map*: cstring
+  ident_map*: cstring
+  strxfrm_multiply*: cuint
+  mbminlen*: cuint
+  mbmaxlen*: cuint
+  min_sort_char*: int16
+  max_sort_char*: int16
+  escape_with_backslash_is_dangerous*: my_bool
+  cset*: pointer
+  coll*: pointer
+
+ + + +
+
+
+
cuint = cint
+
+ + + +
+
+
+
DATA = St_mysql_data
+
+ + + +
+
+
+
Enum_field_types = enum
+  TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE,
+  TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME,
+  TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT,
+  TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248, TYPE_TINY_BLOB = 249,
+  TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251, TYPE_BLOB = 252,
+  TYPE_VAR_STRING = 253, TYPE_STRING = 254, TYPE_GEOMETRY = 255
+
+ + + +
+
+
+
Enum_mysql_stmt_state = enum
+  STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE
+
+ + + +
+
+
+
Enum_server_command {....deprecated.} = enum
+  COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB,
+  COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO,
+  COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME,
+  COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP,
+  COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE,
+  COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION,
+  COM_STMT_FETCH, COM_END
+
+
+ Deprecated +
+ + + +
+
+
+
FIELD = St_mysql_field
+
+ + + +
+
+
+
gptr = cstring
+
+ + + +
+
+
+
MEM_ROOT = St_mem_root
+
+ + + +
+
+
+
my_bool = bool
+
+ + + +
+
+
+
my_socket = cint
+
+ + + +
+
+
+
my_ulonglong = int64
+
+ + + +
+
+
+
NET = St_net
+
+ + + +
+
+
+
Option = enum
+  OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND,
+  READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME,
+  OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT,
+  OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION,
+  OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP, SECURE_AUTH,
+  REPORT_DATA_TRUNCATION, OPT_RECONNECT, MYSQL_OPT_NONBLOCK
+
+ + + +
+
+
+
PBIND = ptr BIND
+
+ + + +
+
+
+
Pcharset_info_st = ptr Charset_info_st
+
+ + + +
+
+
+
PDATA = ptr DATA
+
+ + + +
+
+
+
PFIELD = ptr FIELD
+
+ + + +
+
+
+
Pmy_bool = ptr my_bool
+
+ + + +
+
+
+
PMySQL = ptr St_mysql
+
+ + + +
+
+
+
PNET = ptr NET
+
+ + + +
+
+
+
PPByte = pointer
+
+ + + +
+
+
+
PRES = ptr RES
+
+ + + +
+
+
+
PROWS = ptr ROWS
+
+ + + +
+
+
+
Pst_dynamic_array = ptr St_dynamic_array
+
+ + + +
+
+
+
Pst_mysql = ptr St_mysql
+
+ + + +
+
+
+
Pst_mysql_methods = ptr St_mysql_methods
+
+ + + +
+
+
+
Pst_mysql_rows = ptr St_mysql_rows
+
+ + + +
+
+
+
Pst_mysql_stmt = ptr St_mysql_stmt
+
+ + + +
+
+
+
Pst_used_mem = ptr St_used_mem
+
+ + + +
+
+
+
PSTMT = ptr STMT
+
+ + + +
+
+
+
PUSED_MEM = ptr USED_MEM
+
+ + + +
+
+
+
PVIO = pointer
+
+ + + +
+
+
+
RES = St_mysql_res
+
+ + + +
+
+
+
ROW = cstringArray
+
+ + + +
+
+
+
ROWS = St_mysql_rows
+
+ + + +
+
+
+
St_dynamic_array {.final.} = object
+  buffer*: cstring
+  elements*: cuint
+  max_element*: cuint
+  alloc_increment*: cuint
+  size_of_element*: cuint
+
+ + + +
+
+
+
St_mem_root {.final.} = object
+  free*: PUSED_MEM
+  used*: PUSED_MEM
+  pre_alloc*: PUSED_MEM
+  min_malloc*: cuint
+  block_size*: cuint
+  block_num*: cuint
+  first_block_usage*: cuint
+  error_handler*: proc () {.cdecl.}
+
+ + + +
+
+
+
St_mysql {.final.} = object
+  net*: NET
+  connector_fd*: gptr
+  host*: cstring
+  user*: cstring
+  passwd*: cstring
+  unix_socket*: cstring
+  server_version*: cstring
+  host_info*: cstring
+  info*: cstring
+  db*: cstring
+  charset*: Pcharset_info_st
+  fields*: PFIELD
+  field_alloc*: MEM_ROOT
+  affected_rows*: my_ulonglong
+  insert_id*: my_ulonglong
+  extra_info*: my_ulonglong
+  thread_id*: int
+  packet_length*: int
+  port*: cuint
+  client_flag*: int
+  server_capabilities*: int
+  protocol_version*: cuint
+  field_count*: cuint
+  server_status*: cuint
+  server_language*: cuint
+  warning_count*: cuint
+  options*: St_mysql_options
+  status*: Status
+  free_me*: my_bool
+  reconnect*: my_bool
+  scramble*: array[0 .. 21 - 1, char]
+  rpl_pivot*: my_bool
+  master*: Pst_mysql
+  next_slave*: Pst_mysql
+  last_used_slave*: Pst_mysql
+  last_used_con*: Pst_mysql
+  stmts*: pointer
+  methods*: Pst_mysql_methods
+  thd*: pointer
+  unbuffered_fetch_owner*: Pmy_bool
+
+ + + +
+
+
+
St_mysql_bind {.final.} = object
+  length*: ptr culong
+  is_null*: Pmy_bool
+  buffer*: pointer
+  error*: Pmy_bool
+  u*: U
+  store_param_func*: proc (net: PNET; param: ptr St_mysql_bind) {.cdecl.}
+  fetch_result*: proc (param: ptr St_mysql_bind; field: PFIELD; row: PPByte) {.
+      cdecl.}
+  skip_result*: proc (param: ptr St_mysql_bind; field: PFIELD; row: PPByte) {.
+      cdecl.}
+  buffer_length*: culong
+  offset*: culong
+  length_value*: culong
+  flags*: cuint
+  pack_length*: cuint
+  buffer_type*: Enum_field_types
+  error_value*: my_bool
+  is_unsigned*: my_bool
+  long_data_used*: my_bool
+  is_null_value*: my_bool
+  extension*: pointer
+
+ + + +
+
+
+
St_mysql_data {.final.} = object
+  rows*: my_ulonglong
+  fields*: cuint
+  data*: PROWS
+  alloc*: MEM_ROOT
+  prev_ptr*: ptr PROWS
+
+ + + +
+
+
+
St_mysql_field {.final.} = object
+  name*: cstring
+  org_name*: cstring
+  table*: cstring
+  org_table*: cstring
+  db*: cstring
+  catalog*: cstring
+  def*: cstring
+  len*: int
+  max_length*: int
+  name_length*: cuint
+  org_name_length*: cuint
+  table_length*: cuint
+  org_table_length*: cuint
+  db_length*: cuint
+  catalog_length*: cuint
+  def_length*: cuint
+  flags*: cuint
+  decimals*: cuint
+  charsetnr*: cuint
+  ftype*: Enum_field_types
+  extension*: pointer
+
+ + + +
+
+
+
St_mysql_methods {.final.} = object
+  read_query_result*: proc (MySQL: PMySQL): my_bool {.cdecl.}
+  advanced_command*: proc (MySQL: PMySQL; command: Enum_server_command;
+                           header: cstring; header_length: int; arg: cstring;
+                           arg_length: int; skip_check: my_bool): my_bool
+  read_rows*: proc (MySQL: PMySQL; fields: PFIELD; fields_count: cuint): PDATA
+  use_result*: proc (MySQL: PMySQL): PRES
+  fetch_lengths*: proc (fto: ptr int; column: ROW; field_count: cuint)
+  flush_use_result*: proc (MySQL: PMySQL)
+  list_fields*: proc (MySQL: PMySQL): PFIELD
+  read_prepare_result*: proc (MySQL: PMySQL; stmt: PSTMT): my_bool
+  stmt_execute*: proc (stmt: PSTMT): cint
+  read_binary_rows*: proc (stmt: PSTMT): cint
+  unbuffered_fetch*: proc (MySQL: PMySQL; row: cstringArray): cint
+  free_embedded_thd*: proc (MySQL: PMySQL)
+  read_statistics*: proc (MySQL: PMySQL): cstring
+  next_result*: proc (MySQL: PMySQL): my_bool
+  read_change_user_result*: proc (MySQL: PMySQL; buff: cstring; passwd: cstring): cint
+  read_rowsfrom_cursor*: proc (stmt: PSTMT): cint
+
+ + + +
+
+
+
St_mysql_options {.final.} = object
+  connect_timeout*: cuint
+  read_timeout*: cuint
+  write_timeout*: cuint
+  port*: cuint
+  protocol*: cuint
+  client_flag*: int
+  host*: cstring
+  user*: cstring
+  password*: cstring
+  unix_socket*: cstring
+  db*: cstring
+  init_commands*: Pst_dynamic_array
+  my_cnf_file*: cstring
+  my_cnf_group*: cstring
+  charset_dir*: cstring
+  charset_name*: cstring
+  ssl_key*: cstring
+  ssl_cert*: cstring
+  ssl_ca*: cstring
+  ssl_capath*: cstring
+  ssl_cipher*: cstring
+  shared_memory_base_name*: cstring
+  max_allowed_packet*: int
+  use_ssl*: my_bool
+  compress*: my_bool
+  named_pipe*: my_bool
+  rpl_probe*: my_bool
+  rpl_parse*: my_bool
+  no_master_reads*: my_bool
+  separate_thread*: my_bool
+  methods_to_use*: Option
+  client_ip*: cstring
+  secure_auth*: my_bool
+  report_data_truncation*: my_bool
+  local_infile_init*: proc (para1: var pointer; para2: cstring; para3: pointer): cint {.
+      cdecl.}
+  local_infile_read*: proc (para1: pointer; para2: cstring; para3: cuint): cint
+  local_infile_end*: proc (para1: pointer)
+  local_infile_error*: proc (para1: pointer; para2: cstring; para3: cuint): cint
+  local_infile_userdata*: pointer
+
+ + + +
+
+
+
St_mysql_res {.final.} = object
+  row_count*: my_ulonglong
+  fields*: PFIELD
+  data*: PDATA
+  data_cursor*: PROWS
+  lengths*: ptr int
+  handle*: PMySQL
+  field_alloc*: MEM_ROOT
+  field_count*: cuint
+  current_field*: cuint
+  row*: ROW
+  current_row*: ROW
+  eof*: my_bool
+  unbuffered_fetch_cancelled*: my_bool
+  methods*: Pst_mysql_methods
+
+ + + +
+
+
+
St_mysql_rows {.final.} = object
+  next*: Pst_mysql_rows
+  data*: ROW
+  len*: int
+
+ + + +
+
+
+
St_mysql_stmt {.final.} = object
+  mem_root*: MEM_ROOT
+  mysql*: PMySQL
+  params*: PBIND
+  fields*: PFIELD
+  result*: DATA
+  data_cursor*: PROWS
+  affected_rows*: my_ulonglong
+  insert_id*: my_ulonglong
+  read_row_func*: proc (stmt: Pst_mysql_stmt; row: PPByte): cint {.cdecl.}
+  stmt_id*: int
+  flags*: int
+  prefetch_rows*: int
+  server_status*: cuint
+  last_errno*: cuint
+  param_count*: cuint
+  field_count*: cuint
+  state*: Enum_mysql_stmt_state
+  last_error*: array[0 .. 200 - 1, char]
+  sqlstate*: array[0 .. 6 - 1, char]
+  send_types_to_server*: my_bool
+  bind_param_done*: my_bool
+  bind_result_done*: char
+  unbuffered_fetch_cancelled*: my_bool
+  update_max_length*: my_bool
+
+ + + +
+
+
+
St_net {.final.} = object
+  vio*: PVIO
+  buff*: cstring
+  buff_end*: cstring
+  write_pos*: cstring
+  read_pos*: cstring
+  fd*: my_socket
+  max_packet*: int
+  max_packet_size*: int
+  pkt_nr*: cuint
+  compress_pkt_nr*: cuint
+  write_timeout*: cuint
+  read_timeout*: cuint
+  retry_count*: cuint
+  fcntl*: cint
+  compress*: my_bool
+  remain_in_buf*: int
+  len*: int
+  buf_length*: int
+  where_b*: int
+  return_status*: ptr cint
+  reading_or_writing*: char
+  save_char*: cchar
+  no_send_ok*: my_bool
+  no_send_eof*: my_bool
+  no_send_error*: my_bool
+  last_error*: array[0 .. 200 - 1, char]
+  sqlstate*: array[0 .. 6 - 1, char]
+  last_errno*: cuint
+  error*: char
+  query_cache_query*: gptr
+  report_error*: my_bool
+  return_errno*: my_bool
+
+ + + +
+
+
+
St_used_mem {.final.} = object
+  next*: Pst_used_mem
+  left*: cuint
+  size*: cuint
+
+ + + +
+
+
+
Status = enum
+  STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT
+
+ + + +
+
+
+
STMT = St_mysql_stmt
+
+ + + +
+
+
+
U {.final, union, pure.} = object
+
+ + + +
+
+
+
USED_MEM = St_used_mem
+
+ + + +
+
+ +
+
+
+

Consts

+
+
+
ERRMSG_SIZE = 200
+
+ + + +
+
+
+
NOT_NULL_FLAG = 1
+
+ + + +
+
+
+
PRI_KEY_FLAG = 2
+
+ + + +
+
+
+
SCRAMBLE_LENGTH = 20
+
+ + + +
+
+
+
SQLSTATE_LENGTH = 5
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc close(sock: PMySQL) {.stdcall, dynlib: lib, importc: "mysql_close",
+                           ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc error(MySQL: PMySQL): cstring {.stdcall, dynlib: lib,
+                                     importc: "mysql_error", ...raises: [],
+                                     tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetch_field_direct(res: PRES; fieldnr: cuint): PFIELD {.stdcall,
+    dynlib: lib, importc: "mysql_fetch_field_direct", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetch_row(result: PRES): ROW {.stdcall, dynlib: lib,
+                                    importc: "mysql_fetch_row", ...raises: [],
+                                    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc free_result(result: PRES) {.stdcall, dynlib: lib,
+                                 importc: "mysql_free_result", ...raises: [],
+                                 tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc init(MySQL: PMySQL): PMySQL {.stdcall, dynlib: lib, importc: "mysql_init",
+                                   ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc init(MySQL: var PMySQL) {.stdcall, dynlib: lib, importc: "mysql_init",
+                               ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc num_fields(res: PRES): cuint {.stdcall, dynlib: lib,
+                                    importc: "mysql_num_fields", ...raises: [],
+                                    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc ping(MySQL: PMySQL): cint {.stdcall, dynlib: lib, importc: "mysql_ping",
+                                 ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_connect(MySQL: PMySQL; host: cstring; user: cstring; passwd: cstring;
+                  db: cstring; port: cuint; unix_socket: cstring;
+                  clientflag: int): PMySQL {.stdcall, dynlib: lib,
+    importc: "mysql_real_connect", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_escape_string(MySQL: PMySQL; fto: cstring; from: cstring; len: int): int {.
+    stdcall, dynlib: lib, importc: "mysql_real_escape_string", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc real_query(MySQL: PMySQL; q: cstring; len: int): cint {.stdcall,
+    dynlib: lib, importc: "mysql_real_query", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc stmt_init(MySQL: PMySQL): PSTMT {.stdcall, dynlib: lib,
+                                       importc: "mysql_stmt_init", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc use_result(MySQL: PMySQL): PRES {.stdcall, dynlib: lib,
+                                       importc: "mysql_use_result", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.idx b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.idx new file mode 100644 index 0000000..14c1096 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/mysql/mysql_rdb.idx @@ -0,0 +1,133 @@ +nimTitle mysql_rdb query_builder/libs/mysql/mysql_rdb.html module src/allographer/query_builder/libs/mysql/mysql_rdb 0 +nim false query_builder/libs/mysql/mysql_rdb.html#false my_bool.false 21 +nim true query_builder/libs/mysql/mysql_rdb.html#true my_bool.true 21 +nim my_bool query_builder/libs/mysql/mysql_rdb.html#my_bool type my_bool 21 +nim Pmy_bool query_builder/libs/mysql/mysql_rdb.html#Pmy_bool type Pmy_bool 22 +nim PVIO query_builder/libs/mysql/mysql_rdb.html#PVIO type PVIO 23 +nim gptr query_builder/libs/mysql/mysql_rdb.html#gptr type gptr 25 +nim my_socket query_builder/libs/mysql/mysql_rdb.html#my_socket type my_socket 27 +nim PPByte query_builder/libs/mysql/mysql_rdb.html#PPByte type PPByte 28 +nim cuint query_builder/libs/mysql/mysql_rdb.html#cuint type cuint 29 +nim SQLSTATE_LENGTH query_builder/libs/mysql/mysql_rdb.html#SQLSTATE_LENGTH const SQLSTATE_LENGTH 48 +nim Enum_server_command query_builder/libs/mysql/mysql_rdb.html#Enum_server_command enum Enum_server_command 57 +nim SCRAMBLE_LENGTH query_builder/libs/mysql/mysql_rdb.html#SCRAMBLE_LENGTH const SCRAMBLE_LENGTH 68 +nim NOT_NULL_FLAG query_builder/libs/mysql/mysql_rdb.html#NOT_NULL_FLAG const NOT_NULL_FLAG 75 +nim PRI_KEY_FLAG query_builder/libs/mysql/mysql_rdb.html#PRI_KEY_FLAG const PRI_KEY_FLAG 76 +nim ERRMSG_SIZE query_builder/libs/mysql/mysql_rdb.html#ERRMSG_SIZE const ERRMSG_SIZE 141 +nim St_net query_builder/libs/mysql/mysql_rdb.html#St_net object St_net 158 +nim NET query_builder/libs/mysql/mysql_rdb.html#NET type NET 198 +nim PNET query_builder/libs/mysql/mysql_rdb.html#PNET type PNET 199 +nim TYPE_DECIMAL query_builder/libs/mysql/mysql_rdb.html#TYPE_DECIMAL Enum_field_types.TYPE_DECIMAL 206 +nim TYPE_TINY query_builder/libs/mysql/mysql_rdb.html#TYPE_TINY Enum_field_types.TYPE_TINY 206 +nim TYPE_SHORT query_builder/libs/mysql/mysql_rdb.html#TYPE_SHORT Enum_field_types.TYPE_SHORT 206 +nim TYPE_LONG query_builder/libs/mysql/mysql_rdb.html#TYPE_LONG Enum_field_types.TYPE_LONG 206 +nim TYPE_FLOAT query_builder/libs/mysql/mysql_rdb.html#TYPE_FLOAT Enum_field_types.TYPE_FLOAT 206 +nim TYPE_DOUBLE query_builder/libs/mysql/mysql_rdb.html#TYPE_DOUBLE Enum_field_types.TYPE_DOUBLE 206 +nim TYPE_NULL query_builder/libs/mysql/mysql_rdb.html#TYPE_NULL Enum_field_types.TYPE_NULL 206 +nim TYPE_TIMESTAMP query_builder/libs/mysql/mysql_rdb.html#TYPE_TIMESTAMP Enum_field_types.TYPE_TIMESTAMP 206 +nim TYPE_LONGLONG query_builder/libs/mysql/mysql_rdb.html#TYPE_LONGLONG Enum_field_types.TYPE_LONGLONG 206 +nim TYPE_INT24 query_builder/libs/mysql/mysql_rdb.html#TYPE_INT24 Enum_field_types.TYPE_INT24 206 +nim TYPE_DATE query_builder/libs/mysql/mysql_rdb.html#TYPE_DATE Enum_field_types.TYPE_DATE 206 +nim TYPE_TIME query_builder/libs/mysql/mysql_rdb.html#TYPE_TIME Enum_field_types.TYPE_TIME 206 +nim TYPE_DATETIME query_builder/libs/mysql/mysql_rdb.html#TYPE_DATETIME Enum_field_types.TYPE_DATETIME 206 +nim TYPE_YEAR query_builder/libs/mysql/mysql_rdb.html#TYPE_YEAR Enum_field_types.TYPE_YEAR 206 +nim TYPE_NEWDATE query_builder/libs/mysql/mysql_rdb.html#TYPE_NEWDATE Enum_field_types.TYPE_NEWDATE 206 +nim TYPE_VARCHAR query_builder/libs/mysql/mysql_rdb.html#TYPE_VARCHAR Enum_field_types.TYPE_VARCHAR 206 +nim TYPE_BIT query_builder/libs/mysql/mysql_rdb.html#TYPE_BIT Enum_field_types.TYPE_BIT 206 +nim TYPE_NEWDECIMAL query_builder/libs/mysql/mysql_rdb.html#TYPE_NEWDECIMAL Enum_field_types.TYPE_NEWDECIMAL 206 +nim TYPE_ENUM query_builder/libs/mysql/mysql_rdb.html#TYPE_ENUM Enum_field_types.TYPE_ENUM 206 +nim TYPE_SET query_builder/libs/mysql/mysql_rdb.html#TYPE_SET Enum_field_types.TYPE_SET 206 +nim TYPE_TINY_BLOB query_builder/libs/mysql/mysql_rdb.html#TYPE_TINY_BLOB Enum_field_types.TYPE_TINY_BLOB 206 +nim TYPE_MEDIUM_BLOB query_builder/libs/mysql/mysql_rdb.html#TYPE_MEDIUM_BLOB Enum_field_types.TYPE_MEDIUM_BLOB 206 +nim TYPE_LONG_BLOB query_builder/libs/mysql/mysql_rdb.html#TYPE_LONG_BLOB Enum_field_types.TYPE_LONG_BLOB 206 +nim TYPE_BLOB query_builder/libs/mysql/mysql_rdb.html#TYPE_BLOB Enum_field_types.TYPE_BLOB 206 +nim TYPE_VAR_STRING query_builder/libs/mysql/mysql_rdb.html#TYPE_VAR_STRING Enum_field_types.TYPE_VAR_STRING 206 +nim TYPE_STRING query_builder/libs/mysql/mysql_rdb.html#TYPE_STRING Enum_field_types.TYPE_STRING 206 +nim TYPE_GEOMETRY query_builder/libs/mysql/mysql_rdb.html#TYPE_GEOMETRY Enum_field_types.TYPE_GEOMETRY 206 +nim Enum_field_types query_builder/libs/mysql/mysql_rdb.html#Enum_field_types enum Enum_field_types 206 +nim St_mysql_field query_builder/libs/mysql/mysql_rdb.html#St_mysql_field object St_mysql_field 403 +nim FIELD query_builder/libs/mysql/mysql_rdb.html#FIELD type FIELD 426 +nim PFIELD query_builder/libs/mysql/mysql_rdb.html#PFIELD type PFIELD 427 +nim ROW query_builder/libs/mysql/mysql_rdb.html#ROW type ROW 429 +nim my_ulonglong query_builder/libs/mysql/mysql_rdb.html#my_ulonglong type my_ulonglong 442 +nim Pst_mysql_rows query_builder/libs/mysql/mysql_rdb.html#Pst_mysql_rows type Pst_mysql_rows 449 +nim St_mysql_rows query_builder/libs/mysql/mysql_rdb.html#St_mysql_rows object St_mysql_rows 450 +nim ROWS query_builder/libs/mysql/mysql_rdb.html#ROWS type ROWS 455 +nim PROWS query_builder/libs/mysql/mysql_rdb.html#PROWS type PROWS 456 +nim Pst_used_mem query_builder/libs/mysql/mysql_rdb.html#Pst_used_mem type Pst_used_mem 467 +nim St_used_mem query_builder/libs/mysql/mysql_rdb.html#St_used_mem object St_used_mem 468 +nim USED_MEM query_builder/libs/mysql/mysql_rdb.html#USED_MEM type USED_MEM 473 +nim PUSED_MEM query_builder/libs/mysql/mysql_rdb.html#PUSED_MEM type PUSED_MEM 474 +nim St_mem_root query_builder/libs/mysql/mysql_rdb.html#St_mem_root object St_mem_root 476 +nim MEM_ROOT query_builder/libs/mysql/mysql_rdb.html#MEM_ROOT type MEM_ROOT 488 +nim St_mysql_data query_builder/libs/mysql/mysql_rdb.html#St_mysql_data object St_mysql_data 495 +nim DATA query_builder/libs/mysql/mysql_rdb.html#DATA type DATA 502 +nim PDATA query_builder/libs/mysql/mysql_rdb.html#PDATA type PDATA 503 +nim OPT_CONNECT_TIMEOUT query_builder/libs/mysql/mysql_rdb.html#OPT_CONNECT_TIMEOUT Option.OPT_CONNECT_TIMEOUT 504 +nim OPT_COMPRESS query_builder/libs/mysql/mysql_rdb.html#OPT_COMPRESS Option.OPT_COMPRESS 504 +nim OPT_NAMED_PIPE query_builder/libs/mysql/mysql_rdb.html#OPT_NAMED_PIPE Option.OPT_NAMED_PIPE 504 +nim INIT_COMMAND query_builder/libs/mysql/mysql_rdb.html#INIT_COMMAND Option.INIT_COMMAND 504 +nim READ_DEFAULT_FILE query_builder/libs/mysql/mysql_rdb.html#READ_DEFAULT_FILE Option.READ_DEFAULT_FILE 504 +nim READ_DEFAULT_GROUP query_builder/libs/mysql/mysql_rdb.html#READ_DEFAULT_GROUP Option.READ_DEFAULT_GROUP 504 +nim SET_CHARSET_DIR query_builder/libs/mysql/mysql_rdb.html#SET_CHARSET_DIR Option.SET_CHARSET_DIR 504 +nim SET_CHARSET_NAME query_builder/libs/mysql/mysql_rdb.html#SET_CHARSET_NAME Option.SET_CHARSET_NAME 504 +nim OPT_LOCAL_INFILE query_builder/libs/mysql/mysql_rdb.html#OPT_LOCAL_INFILE Option.OPT_LOCAL_INFILE 504 +nim OPT_PROTOCOL query_builder/libs/mysql/mysql_rdb.html#OPT_PROTOCOL Option.OPT_PROTOCOL 504 +nim SHARED_MEMORY_BASE_NAME query_builder/libs/mysql/mysql_rdb.html#SHARED_MEMORY_BASE_NAME Option.SHARED_MEMORY_BASE_NAME 504 +nim OPT_READ_TIMEOUT query_builder/libs/mysql/mysql_rdb.html#OPT_READ_TIMEOUT Option.OPT_READ_TIMEOUT 504 +nim OPT_WRITE_TIMEOUT query_builder/libs/mysql/mysql_rdb.html#OPT_WRITE_TIMEOUT Option.OPT_WRITE_TIMEOUT 504 +nim OPT_USE_RESULT query_builder/libs/mysql/mysql_rdb.html#OPT_USE_RESULT Option.OPT_USE_RESULT 504 +nim OPT_USE_REMOTE_CONNECTION query_builder/libs/mysql/mysql_rdb.html#OPT_USE_REMOTE_CONNECTION Option.OPT_USE_REMOTE_CONNECTION 504 +nim OPT_USE_EMBEDDED_CONNECTION query_builder/libs/mysql/mysql_rdb.html#OPT_USE_EMBEDDED_CONNECTION Option.OPT_USE_EMBEDDED_CONNECTION 504 +nim OPT_GUESS_CONNECTION query_builder/libs/mysql/mysql_rdb.html#OPT_GUESS_CONNECTION Option.OPT_GUESS_CONNECTION 504 +nim SET_CLIENT_IP query_builder/libs/mysql/mysql_rdb.html#SET_CLIENT_IP Option.SET_CLIENT_IP 504 +nim SECURE_AUTH query_builder/libs/mysql/mysql_rdb.html#SECURE_AUTH Option.SECURE_AUTH 504 +nim REPORT_DATA_TRUNCATION query_builder/libs/mysql/mysql_rdb.html#REPORT_DATA_TRUNCATION Option.REPORT_DATA_TRUNCATION 504 +nim OPT_RECONNECT query_builder/libs/mysql/mysql_rdb.html#OPT_RECONNECT Option.OPT_RECONNECT 504 +nim MYSQL_OPT_NONBLOCK query_builder/libs/mysql/mysql_rdb.html#MYSQL_OPT_NONBLOCK Option.MYSQL_OPT_NONBLOCK 504 +nim Option query_builder/libs/mysql/mysql_rdb.html#Option enum Option 504 +nim St_dynamic_array query_builder/libs/mysql/mysql_rdb.html#St_dynamic_array object St_dynamic_array 524 +nim Pst_dynamic_array query_builder/libs/mysql/mysql_rdb.html#Pst_dynamic_array type Pst_dynamic_array 532 +nim St_mysql_options query_builder/libs/mysql/mysql_rdb.html#St_mysql_options object St_mysql_options 534 +nim STATUS_READY query_builder/libs/mysql/mysql_rdb.html#STATUS_READY Status.STATUS_READY 580 +nim STATUS_GET_RESULT query_builder/libs/mysql/mysql_rdb.html#STATUS_GET_RESULT Status.STATUS_GET_RESULT 580 +nim STATUS_USE_RESULT query_builder/libs/mysql/mysql_rdb.html#STATUS_USE_RESULT Status.STATUS_USE_RESULT 580 +nim Status query_builder/libs/mysql/mysql_rdb.html#Status enum Status 580 +nim Charset_info_st query_builder/libs/mysql/mysql_rdb.html#Charset_info_st object Charset_info_st 589 +nim Pcharset_info_st query_builder/libs/mysql/mysql_rdb.html#Pcharset_info_st type Pcharset_info_st 618 +nim Pst_mysql_methods query_builder/libs/mysql/mysql_rdb.html#Pst_mysql_methods type Pst_mysql_methods 632 +nim Pst_mysql query_builder/libs/mysql/mysql_rdb.html#Pst_mysql type Pst_mysql 633 +nim St_mysql query_builder/libs/mysql/mysql_rdb.html#St_mysql object St_mysql 634 +nim PMySQL query_builder/libs/mysql/mysql_rdb.html#PMySQL type PMySQL 681 +nim St_mysql_res query_builder/libs/mysql/mysql_rdb.html#St_mysql_res object St_mysql_res 683 +nim RES query_builder/libs/mysql/mysql_rdb.html#RES type RES 699 +nim PRES query_builder/libs/mysql/mysql_rdb.html#PRES type PRES 700 +nim Pst_mysql_stmt query_builder/libs/mysql/mysql_rdb.html#Pst_mysql_stmt type Pst_mysql_stmt 701 +nim PSTMT query_builder/libs/mysql/mysql_rdb.html#PSTMT type PSTMT 702 +nim St_mysql_methods query_builder/libs/mysql/mysql_rdb.html#St_mysql_methods object St_mysql_methods 703 +nim STMT_INIT_DONE query_builder/libs/mysql/mysql_rdb.html#STMT_INIT_DONE Enum_mysql_stmt_state.STMT_INIT_DONE 751 +nim STMT_PREPARE_DONE query_builder/libs/mysql/mysql_rdb.html#STMT_PREPARE_DONE Enum_mysql_stmt_state.STMT_PREPARE_DONE 751 +nim STMT_EXECUTE_DONE query_builder/libs/mysql/mysql_rdb.html#STMT_EXECUTE_DONE Enum_mysql_stmt_state.STMT_EXECUTE_DONE 751 +nim STMT_FETCH_DONE query_builder/libs/mysql/mysql_rdb.html#STMT_FETCH_DONE Enum_mysql_stmt_state.STMT_FETCH_DONE 751 +nim Enum_mysql_stmt_state query_builder/libs/mysql/mysql_rdb.html#Enum_mysql_stmt_state enum Enum_mysql_stmt_state 751 +nim U query_builder/libs/mysql/mysql_rdb.html#U object U 775 +nim St_mysql_bind query_builder/libs/mysql/mysql_rdb.html#St_mysql_bind object St_mysql_bind 779 +nim BIND query_builder/libs/mysql/mysql_rdb.html#BIND type BIND 800 +nim PBIND query_builder/libs/mysql/mysql_rdb.html#PBIND type PBIND 801 +nim St_mysql_stmt query_builder/libs/mysql/mysql_rdb.html#St_mysql_stmt object St_mysql_stmt 802 +nim STMT query_builder/libs/mysql/mysql_rdb.html#STMT type STMT 830 +nim num_fields query_builder/libs/mysql/mysql_rdb.html#num_fields,PRES proc num_fields(res: PRES): cuint 873 +nim fetch_field_direct query_builder/libs/mysql/mysql_rdb.html#fetch_field_direct,PRES,cuint proc fetch_field_direct(res: PRES; fieldnr: cuint): PFIELD 876 +nim error query_builder/libs/mysql/mysql_rdb.html#error,PMySQL proc error(MySQL: PMySQL): cstring 891 +nim init query_builder/libs/mysql/mysql_rdb.html#init,PMySQL proc init(MySQL: PMySQL): PMySQL 901 +nim init query_builder/libs/mysql/mysql_rdb.html#init,PMySQL_2 proc init(MySQL: var PMySQL) 902 +nim real_connect query_builder/libs/mysql/mysql_rdb.html#real_connect,PMySQL,cstring,cstring,cstring,cstring,cuint,cstring,int proc real_connect(MySQL: PMySQL; host: cstring; user: cstring; passwd: cstring;\n db: cstring; port: cuint; unix_socket: cstring; clientflag: int): PMySQL 908 +nim real_query query_builder/libs/mysql/mysql_rdb.html#real_query,PMySQL,cstring,int proc real_query(MySQL: PMySQL; q: cstring; len: int): cint 931 +nim use_result query_builder/libs/mysql/mysql_rdb.html#use_result,PMySQL proc use_result(MySQL: PMySQL): PRES 935 +nim ping query_builder/libs/mysql/mysql_rdb.html#ping,PMySQL proc ping(MySQL: PMySQL): cint 991 +nim free_result query_builder/libs/mysql/mysql_rdb.html#free_result,PRES proc free_result(result: PRES) 1013 +nim fetch_row query_builder/libs/mysql/mysql_rdb.html#fetch_row,PRES proc fetch_row(result: PRES): ROW 1021 +nim real_escape_string query_builder/libs/mysql/mysql_rdb.html#real_escape_string,PMySQL,cstring,cstring,int proc real_escape_string(MySQL: PMySQL; fto: cstring; from: cstring; len: int): int 1033 +nim stmt_init query_builder/libs/mysql/mysql_rdb.html#stmt_init,PMySQL proc stmt_init(MySQL: PMySQL): PSTMT 1055 +nim close query_builder/libs/mysql/mysql_rdb.html#close,PMySQL proc close(sock: PMySQL) 1116 +nimgrp init query_builder/libs/mysql/mysql_rdb.html#init-procs-all proc 901 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.html b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.html new file mode 100644 index 0000000..9253ff7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.html @@ -0,0 +1,323 @@ + + + + + + + +src/allographer/query_builder/libs/postgres/postgres_impl + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/postgres/postgres_impl

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

https://www.postgresql.jp/document/12/html/libpq-async.html

+ +
+

Procs

+
+
+
+
proc exec(db: PPGconn; query: string; args: JsonNode; columns: seq[Row];
+          timeout: int): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, KeyError, DbError, ValueError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc exec(db: PPGconn; query: string; args: seq[string]; timeout: int): owned(
+    Future[void]) {....stackTrace: false,
+                    raises: [Exception, DbError, ValueError, OSError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execGetValue(db: PPGconn; query: string; args: JsonNode; columns: seq[Row];
+                  timeout: int): Future[(seq[Row], DbRows)] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, DbError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getColumns(db: PPGconn; query: string; args: seq[string]; timeout: int): Future[
+    seq[string]] {....stackTrace: false,
+                   raises: [Exception, ValueError, DbError, OSError],
+                   tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc prepare(db: PPGconn; query: string; timeout: int; stmtName: string): Future[
+    int] {....stackTrace: false, raises: [Exception, ValueError, DbError, OSError],
+           tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc preparedExec(db: PPGconn; args: seq[string]; nArgs: int; timeout: int;
+                  stmtName: string): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, DbError, ValueError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc preparedQuery(db: PPGconn; args: seq[string]; nArgs: int; timeout: int;
+                   stmtName: string): Future[(seq[Row], DbRows)] {.
+    ...stackTrace: false, raises: [Exception, ValueError, DbError, OSError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc query(db: PPGconn; query: string; args: JsonNode; timeout: int): Future[
+    (seq[Row], DbRows)] {....stackTrace: false, raises: [Exception, ValueError,
+    KeyError, DbError, OSError], tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc query(db: PPGconn; query: string; args: seq[string]; timeout: int): Future[
+    (seq[Row], DbRows)] {....stackTrace: false,
+                          raises: [Exception, ValueError, DbError, OSError],
+                          tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc queryPlain(db: PPGconn; query: string; args: seq[string]; timeout: int): Future[
+    seq[Row]] {....stackTrace: false,
+                raises: [Exception, ValueError, DbError, OSError],
+                tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rawExec(db: PPGconn; query: string; args: JsonNode; timeout: int): owned(
+    Future[void]) {....stackTrace: false,
+                    raises: [Exception, DbError, ValueError, OSError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rawQuery(db: PPGconn; query: string; args: JsonNode; timeout: int): Future[
+    (seq[Row], DbRows)] {....stackTrace: false,
+                          raises: [Exception, ValueError, DbError, OSError],
+                          tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.idx b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.idx new file mode 100644 index 0000000..24512df --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_impl.idx @@ -0,0 +1,15 @@ +nimTitle postgres_impl query_builder/libs/postgres/postgres_impl.html module src/allographer/query_builder/libs/postgres/postgres_impl 0 +nim query query_builder/libs/postgres/postgres_impl.html#query,PPGconn,string,JsonNode,int proc query(db: PPGconn; query: string; args: JsonNode; timeout: int): Future[\n (seq[Row], DbRows)] 132 +nim exec query_builder/libs/postgres/postgres_impl.html#exec,PPGconn,string,JsonNode,seq[Row],int proc exec(db: PPGconn; query: string; args: JsonNode; columns: seq[Row]; timeout: int): owned(\n Future[void]) 160 +nim execGetValue query_builder/libs/postgres/postgres_impl.html#execGetValue,PPGconn,string,JsonNode,seq[Row],int proc execGetValue(db: PPGconn; query: string; args: JsonNode; columns: seq[Row];\n timeout: int): Future[(seq[Row], DbRows)] 176 +nim rawQuery query_builder/libs/postgres/postgres_impl.html#rawQuery,PPGconn,string,JsonNode,int proc rawQuery(db: PPGconn; query: string; args: JsonNode; timeout: int): Future[\n (seq[Row], DbRows)] 204 +nim rawExec query_builder/libs/postgres/postgres_impl.html#rawExec,PPGconn,string,JsonNode,int proc rawExec(db: PPGconn; query: string; args: JsonNode; timeout: int): owned(\n Future[void]) 232 +nim query query_builder/libs/postgres/postgres_impl.html#query,PPGconn,string,seq[string],int proc query(db: PPGconn; query: string; args: seq[string]; timeout: int): Future[\n (seq[Row], DbRows)] 252 +nim queryPlain query_builder/libs/postgres/postgres_impl.html#queryPlain,PPGconn,string,seq[string],int proc queryPlain(db: PPGconn; query: string; args: seq[string]; timeout: int): Future[\n seq[Row]] 277 +nim exec query_builder/libs/postgres/postgres_impl.html#exec,PPGconn,string,seq[string],int proc exec(db: PPGconn; query: string; args: seq[string]; timeout: int): owned(\n Future[void]) 300 +nim getColumns query_builder/libs/postgres/postgres_impl.html#getColumns,PPGconn,string,seq[string],int proc getColumns(db: PPGconn; query: string; args: seq[string]; timeout: int): Future[\n seq[string]] 314 +nim prepare query_builder/libs/postgres/postgres_impl.html#prepare,PPGconn,string,int,string proc prepare(db: PPGconn; query: string; timeout: int; stmtName: string): Future[int] 336 +nim preparedQuery query_builder/libs/postgres/postgres_impl.html#preparedQuery,PPGconn,seq[string],int,int,string proc preparedQuery(db: PPGconn; args: seq[string]; nArgs: int; timeout: int;\n stmtName: string): Future[(seq[Row], DbRows)] 351 +nim preparedExec query_builder/libs/postgres/postgres_impl.html#preparedExec,PPGconn,seq[string],int,int,string proc preparedExec(db: PPGconn; args: seq[string]; nArgs: int; timeout: int;\n stmtName: string): owned(Future[void]) 379 +nimgrp query query_builder/libs/postgres/postgres_impl.html#query-procs-all proc 132 +nimgrp exec query_builder/libs/postgres/postgres_impl.html#exec-procs-all proc 160 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.html b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.html new file mode 100644 index 0000000..640f968 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.html @@ -0,0 +1,314 @@ + + + + + + + +src/allographer/query_builder/libs/postgres/postgres_lib + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/postgres/postgres_lib

+
+ +
+ +
+ +

+ +
+

Types

+
+
+
PGParams = object
+  nParams*: int32
+  values*: cstringArray
+  lengths*: seq[int32]
+  formats*: seq[int32]
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc appendDbRowWithBaseColumns(res: PPGresult; dbRows: var DbRows;
+                                line, cols: int32; base: DbColumns) {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc buildBaseDbColumns(res: PPGresult; cols: int32): DbColumns {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc checkError(db: PPGconn) {....raises: [DbError], tags: [], forbids: [].}
+
+ + Raises a DbError exception. + +
+
+ +
+
+
+
proc dbError(db: PPGconn) {.noreturn, ...raises: [DbError], tags: [], forbids: [].}
+
+ + raises a DbError exception. + +
+
+ +
+
+
+
proc dbFormat(formatstr: string; args: varargs[string]): string {.
+    ...raises: [DbError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromArray(_: type PGParams; args: JsonNode): PGParams
+
+ + args is JArray {true, 1, 1.1, "alice"} + +
+
+ +
+
+
+
proc fromObjArray(_: type PGParams; args: JsonNode): PGParams
+
+ + args is JArray [{"key": "bool", "value": true},{"key": "int", "value": 1}] + +
+
+
+
proc fromObjArray(_: type PGParams; args: JsonNode; columns: seq[Row]): PGParams
+
+ + + +
+
+ +
+
+
+
proc newRow(L: int): Row {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc questionToDaller(s: string): string {....raises: [], tags: [], forbids: [].}
+
+ +

from UPDATE user SET name = ?, email = ? WHERE id = ?

+

to UPDATE user SET name = $1, email = $2 WHERE id = $3

+ + +
+
+ +
+
+
+
proc setColumnInfo(res: PPGresult; dbRows: var DbRows; line, cols: int32) {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc setRow(res: PPGresult; r: var Row; line, cols: int32) {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.idx b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.idx new file mode 100644 index 0000000..e1c0b83 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_lib.idx @@ -0,0 +1,15 @@ +nimTitle postgres_lib query_builder/libs/postgres/postgres_lib.html module src/allographer/query_builder/libs/postgres/postgres_lib 0 +nim dbError query_builder/libs/postgres/postgres_lib.html#dbError,PPGconn proc dbError(db: PPGconn) 8 +nim checkError query_builder/libs/postgres/postgres_lib.html#checkError,PPGconn proc checkError(db: PPGconn) 15 +nim buildBaseDbColumns query_builder/libs/postgres/postgres_lib.html#buildBaseDbColumns,PPGresult,int32 proc buildBaseDbColumns(res: PPGresult; cols: int32): DbColumns 153 +nim appendDbRowWithBaseColumns query_builder/libs/postgres/postgres_lib.html#appendDbRowWithBaseColumns,PPGresult,DbRows,int32,int32,DbColumns proc appendDbRowWithBaseColumns(res: PPGresult; dbRows: var DbRows;\n line, cols: int32; base: DbColumns) 161 +nim setColumnInfo query_builder/libs/postgres/postgres_lib.html#setColumnInfo,PPGresult,DbRows,int32,int32 proc setColumnInfo(res: PPGresult; dbRows: var DbRows; line, cols: int32) 168 +nim newRow query_builder/libs/postgres/postgres_lib.html#newRow,int proc newRow(L: int): Row 172 +nim setRow query_builder/libs/postgres/postgres_lib.html#setRow,PPGresult,Row,int32,int32 proc setRow(res: PPGresult; r: var Row; line, cols: int32) 176 +nim dbFormat query_builder/libs/postgres/postgres_lib.html#dbFormat,string,varargs[string] proc dbFormat(formatstr: string; args: varargs[string]): string 207 +nim questionToDaller query_builder/libs/postgres/postgres_lib.html#questionToDaller,string proc questionToDaller(s: string): string 226 +nim PGParams query_builder/libs/postgres/postgres_lib.html#PGParams object PGParams 245 +nim fromObjArray query_builder/libs/postgres/postgres_lib.html#fromObjArray,typePGParams,JsonNode,seq[Row] proc fromObjArray(_: type PGParams; args: JsonNode; columns: seq[Row]): PGParams 307 +nim fromObjArray query_builder/libs/postgres/postgres_lib.html#fromObjArray,typePGParams,JsonNode proc fromObjArray(_: type PGParams; args: JsonNode): PGParams 313 +nim fromArray query_builder/libs/postgres/postgres_lib.html#fromArray,typePGParams,JsonNode proc fromArray(_: type PGParams; args: JsonNode): PGParams 321 +nimgrp fromobjarray query_builder/libs/postgres/postgres_lib.html#fromObjArray-procs-all proc 307 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.html b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.html new file mode 100644 index 0000000..4cfea23 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.html @@ -0,0 +1,2446 @@ + + + + + + + +src/allographer/query_builder/libs/postgres/postgres_rdb + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/postgres/postgres_rdb

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
ConnStatusType = enum
+  CONNECTION_OK, CONNECTION_BAD, CONNECTION_STARTED, CONNECTION_MADE,
+  CONNECTION_AWAITING_RESPONSE, CONNECTION_AUTH_OK, CONNECTION_SETENV,
+  CONNECTION_SSL_STARTUP, CONNECTION_NEEDED, CONNECTION_CHECK_WRITABLE,
+  CONNECTION_CONSUME, CONNECTION_GSS_STARTUP, CONNECTION_CHECK_TARGET
+
+ + + +
+
+
+
ExecStatusType = enum
+  PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PGRES_COPY_OUT,
+  PGRES_COPY_IN, PGRES_BAD_RESPONSE, PGRES_NONFATAL_ERROR, PGRES_FATAL_ERROR,
+  PGRES_COPY_BOTH, PGRES_SINGLE_TUPLE
+
+ + + +
+
+
+
Oid = int32
+
+ + + +
+
+
+
PConnStatusType = ptr ConnStatusType
+
+ + + +
+
+
+
PExecStatusType = ptr ExecStatusType
+
+ + + +
+
+
+
PGCancel {.pure, final.} = object
+
+ + + +
+
+
+
PGconn {.pure, final.} = object
+  pghost*: cstring
+  pgtty*: cstring
+  pgport*: cstring
+  pgoptions*: cstring
+  dbName*: cstring
+  status*: ConnStatusType
+  errorMessage*: array[0 .. 4096 - 1, char]
+  Pfin*: File
+  Pfout*: File
+  Pfdebug*: File
+  sock*: int32
+  laddr*: SockAddr
+  raddr*: SockAddr
+  salt*: array[0 .. 2 - 1, char]
+  asyncNotifyWaiting*: int32
+  notifyList*: pointer
+  pguser*: cstring
+  pgpass*: cstring
+  lobjfuncs*: PPGlobjfuncs
+
+ + + +
+
+
+
PGlobjfuncs {.pure, final.} = object
+  fn_lo_open*: Oid
+  fn_lo_close*: Oid
+  fn_lo_creat*: Oid
+  fn_lo_unlink*: Oid
+  fn_lo_lseek*: Oid
+  fn_lo_tell*: Oid
+  fn_lo_read*: Oid
+  fn_lo_write*: Oid
+
+ + + +
+
+
+
pgNotify {.pure, final.} = object
+  relname*: cstring
+  be_pid*: int32
+  extra*: cstring
+
+ + + +
+
+
+
PGresAttDesc {.pure, final.} = object
+  name*: cstring
+  adtid*: Oid
+  adtsize*: int
+
+ + + +
+
+
+
PGresAttValue {.pure, final.} = object
+  length*: int32
+  value*: cstring
+
+ + + +
+
+
+
PGresult {.pure, final.} = object
+  ntups*: int32
+  numAttributes*: int32
+  attDescs*: PPGresAttDesc
+  tuples*: PPPGresAttValue
+  tupArrSize*: int32
+  resultStatus*: ExecStatusType
+  cmdStatus*: array[0 .. 40 - 1, char]
+  binary*: int32
+  conn*: PPGconn
+
+ + + +
+
+
+
PGTransactionStatusType = enum
+  PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR,
+  PQTRANS_UNKNOWN
+
+ + + +
+
+
+
PGVerbosity = enum
+  PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE, PQERRORS_SQLSTATE
+
+ + + +
+
+
+
POid = ptr Oid
+
+ + + +
+
+
+
PostgresPollingStatusType = enum
+  PGRES_POLLING_FAILED = 0, PGRES_POLLING_READING, PGRES_POLLING_WRITING,
+  PGRES_POLLING_OK, PGRES_POLLING_ACTIVE
+
+ + + +
+
+
+
PPGcancel = ptr PGCancel
+
+ + + +
+
+
+
PPGconn = ptr PGconn
+
+ + + +
+
+
+
PPGlobjfuncs = ptr PGlobjfuncs
+
+ + + +
+
+
+
PPGNotify = ptr pgNotify
+
+ + + +
+
+
+
PPGresAttDesc = ptr PGresAttDesc
+
+ + + +
+
+
+
PPGresAttValue = ptr PGresAttValue
+
+ + + +
+
+
+
PPGresult = ptr PGresult
+
+ + + +
+
+ +
+
PPGVerbosity = ptr PGVerbosity
+
+ + + +
+
+ +
+
PPPGresAttDesc = ptr PPGresAttDesc
+
+ + + +
+
+
+
PPPGresAttValue = ptr PPGresAttValue
+
+ + + +
+
+
+
PPQArgBlock = ptr PQArgBlock
+
+ + + +
+
+
+
Ppqbool = ptr pqbool
+
+ + + +
+
+
+
PPQconninfoOption = ptr PQconninfoOption
+
+ + + +
+
+
+
PPQprintOpt = ptr PQprintOpt
+
+ + + +
+
+
+
PQArgBlock {.pure, final.} = object
+  length*: int32
+  isint*: int32
+  p*: pointer
+
+ + + +
+
+
+
pqbool = char
+
+ + + +
+
+
+
PQconninfoOption {.pure, final.} = object
+  keyword*: cstring
+  envvar*: cstring
+  compiled*: cstring
+  val*: cstring
+  label*: cstring
+  dispchar*: cstring
+  dispsize*: int32
+
+ + + +
+
+
+
PQnoticeProcessor = proc (arg: pointer; message: cstring) {.cdecl.}
+
+ + + +
+
+
+
PQnoticeReceiver = proc (arg: pointer; res: PPGresult) {.cdecl.}
+
+ + + +
+
+
+
PQprintOpt {.pure, final.} = object
+  header*: pqbool
+  align*: pqbool
+  standard*: pqbool
+  html3*: pqbool
+  expanded*: pqbool
+  pager*: pqbool
+  fieldSep*: cstring
+  tableOpt*: cstring
+  caption*: cstring
+  fieldName*: ptr cstring
+
+ + + +
+
+
+
SockAddr = array[1 .. 112, int8]
+
+ + + +
+
+ +
+
+
+

Consts

+
+
+
CMDSTATUS_LEN = 40
+
+ + + +
+
+
+
ERROR_MSG_LENGTH = 4096
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc lo_close(conn: PPGconn; fd: int32): int32 {.cdecl, dynlib: dllName,
+    importc: "lo_close", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc lo_creat(conn: PPGconn; mode: int32): Oid {.cdecl, dynlib: dllName,
+    importc: "lo_creat", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc lo_export(conn: PPGconn; lobjId: Oid; filename: cstring): int32 {.cdecl,
+    dynlib: dllName, importc: "lo_export", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc lo_import(conn: PPGconn; filename: cstring): Oid {.cdecl, dynlib: dllName,
+    importc: "lo_import", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc lo_lseek(conn: PPGconn; fd: int32; offset: int32; whence: int32): int32 {.
+    cdecl, dynlib: dllName, importc: "lo_lseek", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc lo_open(conn: PPGconn; lobjId: Oid; mode: int32): int32 {.cdecl,
+    dynlib: dllName, importc: "lo_open", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc lo_read(conn: PPGconn; fd: int32; buf: cstring; length: int): int32 {.
+    cdecl, dynlib: dllName, importc: "lo_read", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc lo_tell(conn: PPGconn; fd: int32): int32 {.cdecl, dynlib: dllName,
+    importc: "lo_tell", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+
proc lo_write(conn: PPGconn; fd: int32; buf: cstring; length: int): int32 {.
+    cdecl, dynlib: dllName, importc: "lo_write", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqbackendPID(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQbackendPID", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqbinaryTuples(res: PPGresult): int32 {.cdecl, dynlib: dllName,
+    importc: "PQbinaryTuples", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqCancel(cancel: PPGcancel; errbuf: cstring; errbufsize: int32): int32 {.
+    cdecl, dynlib: dllName, importc: "PQcancel", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqclear(res: PPGresult) {.cdecl, dynlib: dllName, importc: "PQclear",
+                               ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqclientEncoding(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQclientEncoding", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqcmdStatus(res: PPGresult): cstring {.cdecl, dynlib: dllName,
+    importc: "PQcmdStatus", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqcmdTuples(res: PPGresult): cstring {.cdecl, dynlib: dllName,
+    importc: "PQcmdTuples", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconndefaults(): PPQconninfoOption {.cdecl, dynlib: dllName,
+    importc: "PQconndefaults", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconnectdb(conninfo: cstring): PPGconn {.cdecl, dynlib: dllName,
+    importc: "PQconnectdb", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconnectionNeedsPassword(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQconnectionNeedsPassword", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconnectionUsedPassword(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQconnectionUsedPassword", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconnectPoll(conn: PPGconn): PostgresPollingStatusType {.cdecl,
+    dynlib: dllName, importc: "PQconnectPoll", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconnectStart(conninfo: cstring): PPGconn {.cdecl, dynlib: dllName,
+    importc: "PQconnectStart", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconninfoFree(connOptions: PPQconninfoOption) {.cdecl, dynlib: dllName,
+    importc: "PQconninfoFree", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqconsumeInput(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQconsumeInput", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqdb(conn: PPGconn): cstring {.cdecl, dynlib: dllName, importc: "PQdb",
+                                    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqdisplayTuples(res: PPGresult; fp: File; fillAlign: int32;
+                     fieldSep: cstring; printHeader: int32; quiet: int32) {.
+    cdecl, dynlib: dllName, importc: "PQdisplayTuples", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqendcopy(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+                                       importc: "PQendcopy", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqenv2encoding(): int32 {.cdecl, dynlib: dllName,
+                               importc: "PQenv2encoding", ...raises: [], tags: [],
+                               forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqerrorMessage(conn: PPGconn): cstring {.cdecl, dynlib: dllName,
+    importc: "PQerrorMessage", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqescapeBytea(bintext: cstring; binlen: int; bytealen: var int): cstring {.
+    cdecl, dynlib: dllName, importc: "PQescapeBytea", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqescapeString(till, from: cstring; len: int): int {.cdecl,
+    dynlib: dllName, importc: "PQescapeString", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqexec(conn: PPGconn; query: cstring): PPGresult {.cdecl, dynlib: dllName,
+    importc: "PQexec", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqexecParams(conn: PPGconn; command: cstring; nParams: int32;
+                  paramTypes: POid; paramValues: cstringArray;
+                  paramLengths, paramFormats: ptr int32; resultFormat: int32): PPGresult {.
+    cdecl, dynlib: dllName, importc: "PQexecParams", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqexecPrepared(conn: PPGconn; stmtName: cstring; nParams: int32;
+                    paramValues: cstringArray;
+                    paramLengths, paramFormats: ptr int32; resultFormat: int32): PPGresult {.
+    cdecl, dynlib: dllName, importc: "PQexecPrepared", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfformat(res: PPGresult; field_num: int32): int32 {.cdecl,
+    dynlib: dllName, importc: "PQfformat", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfinish(conn: PPGconn) {.cdecl, dynlib: dllName, importc: "PQfinish",
+                               ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqflush(conn: PPGconn): int32 {.cdecl, dynlib: dllName, importc: "PQflush",
+                                     ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfmod(res: PPGresult; field_num: int32): int32 {.cdecl, dynlib: dllName,
+    importc: "PQfmod", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfn(conn: PPGconn; fnid: int32; result_buf, result_len: ptr int32;
+          result_is_int: int32; args: PPQArgBlock; nargs: int32): PPGresult {.
+    cdecl, dynlib: dllName, importc: "PQfn", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfname(res: PPGresult; field_num: int32): cstring {.cdecl,
+    dynlib: dllName, importc: "PQfname", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfnumber(res: PPGresult; field_name: cstring): int32 {.cdecl,
+    dynlib: dllName, importc: "PQfnumber", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqFreeCancel(cancel: PPGcancel) {.cdecl, dynlib: dllName,
+                                       importc: "PQfreeCancel", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfreemem(p: pointer) {.cdecl, dynlib: dllName, importc: "PQfreemem",
+                             ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqfsize(res: PPGresult; field_num: int32): int32 {.cdecl, dynlib: dllName,
+    importc: "PQfsize", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqftable(res: PPGresult; field_num: int32): Oid {.cdecl, dynlib: dllName,
+    importc: "PQftable", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqftablecol(res: PPGresult; field_num: int32): int32 {.cdecl,
+    dynlib: dllName, importc: "PQftablecol", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqftype(res: PPGresult; field_num: int32): Oid {.cdecl, dynlib: dllName,
+    importc: "PQftype", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqGetCancel(conn: PPGconn): PPGcancel {.cdecl, dynlib: dllName,
+    importc: "PQgetCancel", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqgetCopyData(conn: PPGconn; buffer: cstringArray; async: int32): int32 {.
+    cdecl, dynlib: dllName, importc: "PQgetCopyData", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqgetisnull(res: PPGresult; tup_num: int32; field_num: int32): int32 {.
+    cdecl, dynlib: dllName, importc: "PQgetisnull", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqgetlength(res: PPGresult; tup_num: int32; field_num: int32): int32 {.
+    cdecl, dynlib: dllName, importc: "PQgetlength", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqgetline(conn: PPGconn; str: cstring; len: int32): int32 {.cdecl,
+    dynlib: dllName, importc: "PQgetline", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqgetlineAsync(conn: PPGconn; buffer: cstring; bufsize: int32): int32 {.
+    cdecl, dynlib: dllName, importc: "PQgetlineAsync", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqgetResult(conn: PPGconn): PPGresult {.cdecl, dynlib: dllName,
+    importc: "PQgetResult", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqgetvalue(res: PPGresult; tup_num: int32; field_num: int32): cstring {.
+    cdecl, dynlib: dllName, importc: "PQgetvalue", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqhost(conn: PPGconn): cstring {.cdecl, dynlib: dllName, importc: "PQhost",
+                                      ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqinitOpenSSL(do_ssl: int32; do_crypto: int32) {.cdecl, dynlib: dllName,
+    importc: "PQinitOpenSSL", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqisBusy(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+                                      importc: "PQisBusy", ...raises: [], tags: [],
+                                      forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqisnonblocking(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQisnonblocking", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqmakeEmptyPGresult(conn: PPGconn; status: ExecStatusType): PPGresult {.
+    cdecl, dynlib: dllName, importc: "PQmakeEmptyPGresult", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqmblen(s: cstring; encoding: int32): int32 {.cdecl, dynlib: dllName,
+    importc: "PQmblen", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqnfields(res: PPGresult): int32 {.cdecl, dynlib: dllName,
+                                        importc: "PQnfields", ...raises: [],
+                                        tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqnotifies(conn: PPGconn): PPGNotify {.cdecl, dynlib: dllName,
+    importc: "PQnotifies", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqntuples(res: PPGresult): int32 {.cdecl, dynlib: dllName,
+                                        importc: "PQntuples", ...raises: [],
+                                        tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqoidStatus(res: PPGresult): cstring {.cdecl, dynlib: dllName,
+    importc: "PQoidStatus", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqoidValue(res: PPGresult): Oid {.cdecl, dynlib: dllName,
+                                       importc: "PQoidValue", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqoptions(conn: PPGconn): cstring {.cdecl, dynlib: dllName,
+    importc: "PQoptions", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqparameterStatus(conn: PPGconn; paramName: cstring): cstring {.cdecl,
+    dynlib: dllName, importc: "PQparameterStatus", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqpass(conn: PPGconn): cstring {.cdecl, dynlib: dllName, importc: "PQpass",
+                                      ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqport(conn: PPGconn): cstring {.cdecl, dynlib: dllName, importc: "PQport",
+                                      ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqprepare(conn: PPGconn; stmtName, query: cstring; nParams: int32;
+               paramTypes: POid): PPGresult {.cdecl, dynlib: dllName,
+    importc: "PQprepare", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqprint(fout: File; res: PPGresult; ps: PPQprintOpt) {.cdecl,
+    dynlib: dllName, importc: "PQprint", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqprintTuples(res: PPGresult; fout: File; printAttName: int32;
+                   terseOutput: int32; width: int32) {.cdecl, dynlib: dllName,
+    importc: "PQprintTuples", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqprotocolVersion(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQprotocolVersion", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqputCopyData(conn: PPGconn; buffer: cstring; nbytes: int32): int32 {.
+    cdecl, dynlib: dllName, importc: "PQputCopyData", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqputCopyEnd(conn: PPGconn; errormsg: cstring): int32 {.cdecl,
+    dynlib: dllName, importc: "PQputCopyEnd", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqputline(conn: PPGconn; str: cstring): int32 {.cdecl, dynlib: dllName,
+    importc: "PQputline", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqputnbytes(conn: PPGconn; buffer: cstring; nbytes: int32): int32 {.cdecl,
+    dynlib: dllName, importc: "PQputnbytes", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqrequestCancel(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQrequestCancel", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqreset(conn: PPGconn) {.cdecl, dynlib: dllName, importc: "PQreset",
+                              ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqresetPoll(conn: PPGconn): PostgresPollingStatusType {.cdecl,
+    dynlib: dllName, importc: "PQresetPoll", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqresetStart(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQresetStart", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqresStatus(status: ExecStatusType): cstring {.cdecl, dynlib: dllName,
+    importc: "PQresStatus", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqresultErrorField(res: PPGresult; fieldcode: int32): cstring {.cdecl,
+    dynlib: dllName, importc: "PQresultErrorField", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqresultErrorMessage(res: PPGresult): cstring {.cdecl, dynlib: dllName,
+    importc: "PQresultErrorMessage", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqresultStatus(res: PPGresult): ExecStatusType {.cdecl, dynlib: dllName,
+    importc: "PQresultStatus", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsendPrepare(conn: PPGconn; stmtName: cstring; query: cstring;
+                   nParams: int32; paramTypes: POid): int32 {.cdecl,
+    dynlib: dllName, importc: "PQsendPrepare", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsendQuery(conn: PPGconn; query: cstring): int32 {.cdecl, dynlib: dllName,
+    importc: "PQsendQuery", ...raises: [], tags: [], forbids: [].}
+
+ + See also https://www.postgresql.org/docs/current/libpq-async.html + +
+
+ +
+
+
+
proc pqsendQueryParams(conn: PPGconn; command: cstring; nParams: int32;
+                       paramTypes: POid; paramValues: cstringArray;
+                       paramLengths, paramFormats: ptr int32;
+                       resultFormat: int32): int32 {.cdecl, dynlib: dllName,
+    importc: "PQsendQueryParams", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsendQueryPrepared(conn: PPGconn; stmtName: cstring; nParams: int32;
+                         paramValues: cstringArray;
+                         paramLengths, paramFormats: ptr int32;
+                         resultFormat: int32): int32 {.cdecl, dynlib: dllName,
+    importc: "PQsendQueryPrepared", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqserverVersion(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQserverVersion", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsetClientEncoding(conn: PPGconn; encoding: cstring): int32 {.cdecl,
+    dynlib: dllName, importc: "PQsetClientEncoding", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsetdbLogin(pghost: cstring; pgport: cstring; pgoptions: cstring;
+                  pgtty: cstring; dbName: cstring; login: cstring; pwd: cstring): PPGconn {.
+    cdecl, dynlib: dllName, importc: "PQsetdbLogin", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsetErrorVerbosity(conn: PPGconn; verbosity: PGVerbosity): PGVerbosity {.
+    cdecl, dynlib: dllName, importc: "PQsetErrorVerbosity", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsetnonblocking(conn: PPGconn; arg: int32): int32 {.cdecl,
+    dynlib: dllName, importc: "PQsetnonblocking", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsetNoticeProcessor(conn: PPGconn; theProc: PQnoticeProcessor;
+                          arg: pointer): PQnoticeProcessor {.cdecl,
+    dynlib: dllName, importc: "PQsetNoticeProcessor", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqsetNoticeReceiver(conn: PPGconn; theProc: PQnoticeReceiver; arg: pointer): PQnoticeReceiver {.
+    cdecl, dynlib: dllName, importc: "PQsetNoticeReceiver", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqSetSingleRowMode(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+    importc: "PQsetSingleRowMode", ...raises: [], tags: [], forbids: [].}
+
+ + See also https://www.postgresql.org/docs/current/libpq-single-row-mode.html + +
+
+ +
+
+
+
proc pqsocket(conn: PPGconn): int32 {.cdecl, dynlib: dllName,
+                                      importc: "PQsocket", ...raises: [], tags: [],
+                                      forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqstatus(conn: PPGconn): ConnStatusType {.cdecl, dynlib: dllName,
+    importc: "PQstatus", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqtrace(conn: PPGconn; debug_port: File) {.cdecl, dynlib: dllName,
+    importc: "PQtrace", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqtransactionStatus(conn: PPGconn): PGTransactionStatusType {.cdecl,
+    dynlib: dllName, importc: "PQtransactionStatus", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqtty(conn: PPGconn): cstring {.cdecl, dynlib: dllName, importc: "PQtty",
+                                     ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pqunescapeBytea(strtext: cstring; retbuflen: var int): cstring {.cdecl,
+    dynlib: dllName, importc: "PQunescapeBytea", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pquntrace(conn: PPGconn) {.cdecl, dynlib: dllName, importc: "PQuntrace",
+                                ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc pquser(conn: PPGconn): cstring {.cdecl, dynlib: dllName, importc: "PQuser",
+                                      ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.idx b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.idx new file mode 100644 index 0000000..98f5259 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/postgres/postgres_rdb.idx @@ -0,0 +1,187 @@ +nimTitle postgres_rdb query_builder/libs/postgres/postgres_rdb.html module src/allographer/query_builder/libs/postgres/postgres_rdb 0 +nim POid query_builder/libs/postgres/postgres_rdb.html#POid type POid 14 +nim Oid query_builder/libs/postgres/postgres_rdb.html#Oid type Oid 15 +nim ERROR_MSG_LENGTH query_builder/libs/postgres/postgres_rdb.html#ERROR_MSG_LENGTH const ERROR_MSG_LENGTH 18 +nim CMDSTATUS_LEN query_builder/libs/postgres/postgres_rdb.html#CMDSTATUS_LEN const CMDSTATUS_LEN 19 +nim SockAddr query_builder/libs/postgres/postgres_rdb.html#SockAddr type SockAddr 22 +nim PGresAttDesc query_builder/libs/postgres/postgres_rdb.html#PGresAttDesc object PGresAttDesc 23 +nim PPGresAttDesc query_builder/libs/postgres/postgres_rdb.html#PPGresAttDesc type PPGresAttDesc 28 +nim PPPGresAttDesc query_builder/libs/postgres/postgres_rdb.html#PPPGresAttDesc type PPPGresAttDesc 29 +nim PGresAttValue query_builder/libs/postgres/postgres_rdb.html#PGresAttValue object PGresAttValue 30 +nim PPGresAttValue query_builder/libs/postgres/postgres_rdb.html#PPGresAttValue type PPGresAttValue 34 +nim PPPGresAttValue query_builder/libs/postgres/postgres_rdb.html#PPPGresAttValue type PPPGresAttValue 35 +nim PExecStatusType query_builder/libs/postgres/postgres_rdb.html#PExecStatusType type PExecStatusType 36 +nim PGRES_EMPTY_QUERY query_builder/libs/postgres/postgres_rdb.html#PGRES_EMPTY_QUERY ExecStatusType.PGRES_EMPTY_QUERY 37 +nim PGRES_COMMAND_OK query_builder/libs/postgres/postgres_rdb.html#PGRES_COMMAND_OK ExecStatusType.PGRES_COMMAND_OK 37 +nim PGRES_TUPLES_OK query_builder/libs/postgres/postgres_rdb.html#PGRES_TUPLES_OK ExecStatusType.PGRES_TUPLES_OK 37 +nim PGRES_COPY_OUT query_builder/libs/postgres/postgres_rdb.html#PGRES_COPY_OUT ExecStatusType.PGRES_COPY_OUT 37 +nim PGRES_COPY_IN query_builder/libs/postgres/postgres_rdb.html#PGRES_COPY_IN ExecStatusType.PGRES_COPY_IN 37 +nim PGRES_BAD_RESPONSE query_builder/libs/postgres/postgres_rdb.html#PGRES_BAD_RESPONSE ExecStatusType.PGRES_BAD_RESPONSE 37 +nim PGRES_NONFATAL_ERROR query_builder/libs/postgres/postgres_rdb.html#PGRES_NONFATAL_ERROR ExecStatusType.PGRES_NONFATAL_ERROR 37 +nim PGRES_FATAL_ERROR query_builder/libs/postgres/postgres_rdb.html#PGRES_FATAL_ERROR ExecStatusType.PGRES_FATAL_ERROR 37 +nim PGRES_COPY_BOTH query_builder/libs/postgres/postgres_rdb.html#PGRES_COPY_BOTH ExecStatusType.PGRES_COPY_BOTH 37 +nim PGRES_SINGLE_TUPLE query_builder/libs/postgres/postgres_rdb.html#PGRES_SINGLE_TUPLE ExecStatusType.PGRES_SINGLE_TUPLE 37 +nim ExecStatusType query_builder/libs/postgres/postgres_rdb.html#ExecStatusType enum ExecStatusType 37 +nim PGlobjfuncs query_builder/libs/postgres/postgres_rdb.html#PGlobjfuncs object PGlobjfuncs 41 +nim PPGlobjfuncs query_builder/libs/postgres/postgres_rdb.html#PPGlobjfuncs type PPGlobjfuncs 51 +nim PConnStatusType query_builder/libs/postgres/postgres_rdb.html#PConnStatusType type PConnStatusType 52 +nim CONNECTION_OK query_builder/libs/postgres/postgres_rdb.html#CONNECTION_OK ConnStatusType.CONNECTION_OK 53 +nim CONNECTION_BAD query_builder/libs/postgres/postgres_rdb.html#CONNECTION_BAD ConnStatusType.CONNECTION_BAD 53 +nim CONNECTION_STARTED query_builder/libs/postgres/postgres_rdb.html#CONNECTION_STARTED ConnStatusType.CONNECTION_STARTED 53 +nim CONNECTION_MADE query_builder/libs/postgres/postgres_rdb.html#CONNECTION_MADE ConnStatusType.CONNECTION_MADE 53 +nim CONNECTION_AWAITING_RESPONSE query_builder/libs/postgres/postgres_rdb.html#CONNECTION_AWAITING_RESPONSE ConnStatusType.CONNECTION_AWAITING_RESPONSE 53 +nim CONNECTION_AUTH_OK query_builder/libs/postgres/postgres_rdb.html#CONNECTION_AUTH_OK ConnStatusType.CONNECTION_AUTH_OK 53 +nim CONNECTION_SETENV query_builder/libs/postgres/postgres_rdb.html#CONNECTION_SETENV ConnStatusType.CONNECTION_SETENV 53 +nim CONNECTION_SSL_STARTUP query_builder/libs/postgres/postgres_rdb.html#CONNECTION_SSL_STARTUP ConnStatusType.CONNECTION_SSL_STARTUP 53 +nim CONNECTION_NEEDED query_builder/libs/postgres/postgres_rdb.html#CONNECTION_NEEDED ConnStatusType.CONNECTION_NEEDED 53 +nim CONNECTION_CHECK_WRITABLE query_builder/libs/postgres/postgres_rdb.html#CONNECTION_CHECK_WRITABLE ConnStatusType.CONNECTION_CHECK_WRITABLE 53 +nim CONNECTION_CONSUME query_builder/libs/postgres/postgres_rdb.html#CONNECTION_CONSUME ConnStatusType.CONNECTION_CONSUME 53 +nim CONNECTION_GSS_STARTUP query_builder/libs/postgres/postgres_rdb.html#CONNECTION_GSS_STARTUP ConnStatusType.CONNECTION_GSS_STARTUP 53 +nim CONNECTION_CHECK_TARGET query_builder/libs/postgres/postgres_rdb.html#CONNECTION_CHECK_TARGET ConnStatusType.CONNECTION_CHECK_TARGET 53 +nim ConnStatusType query_builder/libs/postgres/postgres_rdb.html#ConnStatusType enum ConnStatusType 53 +nim PGconn query_builder/libs/postgres/postgres_rdb.html#PGconn object PGconn 58 +nim PPGconn query_builder/libs/postgres/postgres_rdb.html#PPGconn type PPGconn 79 +nim PGresult query_builder/libs/postgres/postgres_rdb.html#PGresult object PGresult 80 +nim PGCancel query_builder/libs/postgres/postgres_rdb.html#PGCancel object PGCancel 91 +nim PPGcancel query_builder/libs/postgres/postgres_rdb.html#PPGcancel type PPGcancel 92 +nim PPGresult query_builder/libs/postgres/postgres_rdb.html#PPGresult type PPGresult 94 +nim PPostgresPollingStatusType query_builder/libs/postgres/postgres_rdb.html#PPostgresPollingStatusType type PPostgresPollingStatusType 95 +nim PGRES_POLLING_FAILED query_builder/libs/postgres/postgres_rdb.html#PGRES_POLLING_FAILED PostgresPollingStatusType.PGRES_POLLING_FAILED 96 +nim PGRES_POLLING_READING query_builder/libs/postgres/postgres_rdb.html#PGRES_POLLING_READING PostgresPollingStatusType.PGRES_POLLING_READING 96 +nim PGRES_POLLING_WRITING query_builder/libs/postgres/postgres_rdb.html#PGRES_POLLING_WRITING PostgresPollingStatusType.PGRES_POLLING_WRITING 96 +nim PGRES_POLLING_OK query_builder/libs/postgres/postgres_rdb.html#PGRES_POLLING_OK PostgresPollingStatusType.PGRES_POLLING_OK 96 +nim PGRES_POLLING_ACTIVE query_builder/libs/postgres/postgres_rdb.html#PGRES_POLLING_ACTIVE PostgresPollingStatusType.PGRES_POLLING_ACTIVE 96 +nim PostgresPollingStatusType query_builder/libs/postgres/postgres_rdb.html#PostgresPollingStatusType enum PostgresPollingStatusType 96 +nim PPGTransactionStatusType query_builder/libs/postgres/postgres_rdb.html#PPGTransactionStatusType type PPGTransactionStatusType 99 +nim PQTRANS_IDLE query_builder/libs/postgres/postgres_rdb.html#PQTRANS_IDLE PGTransactionStatusType.PQTRANS_IDLE 100 +nim PQTRANS_ACTIVE query_builder/libs/postgres/postgres_rdb.html#PQTRANS_ACTIVE PGTransactionStatusType.PQTRANS_ACTIVE 100 +nim PQTRANS_INTRANS query_builder/libs/postgres/postgres_rdb.html#PQTRANS_INTRANS PGTransactionStatusType.PQTRANS_INTRANS 100 +nim PQTRANS_INERROR query_builder/libs/postgres/postgres_rdb.html#PQTRANS_INERROR PGTransactionStatusType.PQTRANS_INERROR 100 +nim PQTRANS_UNKNOWN query_builder/libs/postgres/postgres_rdb.html#PQTRANS_UNKNOWN PGTransactionStatusType.PQTRANS_UNKNOWN 100 +nim PGTransactionStatusType query_builder/libs/postgres/postgres_rdb.html#PGTransactionStatusType enum PGTransactionStatusType 100 +nim PPGVerbosity query_builder/libs/postgres/postgres_rdb.html#PPGVerbosity type PPGVerbosity 103 +nim PQERRORS_TERSE query_builder/libs/postgres/postgres_rdb.html#PQERRORS_TERSE PGVerbosity.PQERRORS_TERSE 104 +nim PQERRORS_DEFAULT query_builder/libs/postgres/postgres_rdb.html#PQERRORS_DEFAULT PGVerbosity.PQERRORS_DEFAULT 104 +nim PQERRORS_VERBOSE query_builder/libs/postgres/postgres_rdb.html#PQERRORS_VERBOSE PGVerbosity.PQERRORS_VERBOSE 104 +nim PQERRORS_SQLSTATE query_builder/libs/postgres/postgres_rdb.html#PQERRORS_SQLSTATE PGVerbosity.PQERRORS_SQLSTATE 104 +nim PGVerbosity query_builder/libs/postgres/postgres_rdb.html#PGVerbosity enum PGVerbosity 104 +nim PPGNotify query_builder/libs/postgres/postgres_rdb.html#PPGNotify type PPGNotify 106 +nim pgNotify query_builder/libs/postgres/postgres_rdb.html#pgNotify object pgNotify 107 +nim PQnoticeReceiver query_builder/libs/postgres/postgres_rdb.html#PQnoticeReceiver type PQnoticeReceiver 112 +nim PQnoticeProcessor query_builder/libs/postgres/postgres_rdb.html#PQnoticeProcessor type PQnoticeProcessor 113 +nim Ppqbool query_builder/libs/postgres/postgres_rdb.html#Ppqbool type Ppqbool 114 +nim pqbool query_builder/libs/postgres/postgres_rdb.html#pqbool type pqbool 115 +nim PPQprintOpt query_builder/libs/postgres/postgres_rdb.html#PPQprintOpt type PPQprintOpt 116 +nim PQprintOpt query_builder/libs/postgres/postgres_rdb.html#PQprintOpt object PQprintOpt 117 +nim PPQconninfoOption query_builder/libs/postgres/postgres_rdb.html#PPQconninfoOption type PPQconninfoOption 129 +nim PQconninfoOption query_builder/libs/postgres/postgres_rdb.html#PQconninfoOption object PQconninfoOption 130 +nim PPQArgBlock query_builder/libs/postgres/postgres_rdb.html#PPQArgBlock type PPQArgBlock 139 +nim PQArgBlock query_builder/libs/postgres/postgres_rdb.html#PQArgBlock object PQArgBlock 140 +nim pqinitOpenSSL query_builder/libs/postgres/postgres_rdb.html#pqinitOpenSSL,int32,int32 proc pqinitOpenSSL(do_ssl: int32; do_crypto: int32) 145 +nim pqconnectStart query_builder/libs/postgres/postgres_rdb.html#pqconnectStart,cstring proc pqconnectStart(conninfo: cstring): PPGconn 147 +nim pqconnectPoll query_builder/libs/postgres/postgres_rdb.html#pqconnectPoll,PPGconn proc pqconnectPoll(conn: PPGconn): PostgresPollingStatusType 149 +nim pqconnectdb query_builder/libs/postgres/postgres_rdb.html#pqconnectdb,cstring proc pqconnectdb(conninfo: cstring): PPGconn 151 +nim pqsetdbLogin query_builder/libs/postgres/postgres_rdb.html#pqsetdbLogin,cstring,cstring,cstring,cstring,cstring,cstring,cstring proc pqsetdbLogin(pghost: cstring; pgport: cstring; pgoptions: cstring;\n pgtty: cstring; dbName: cstring; login: cstring; pwd: cstring): PPGconn 153 +nim pqsetdb query_builder/libs/postgres/postgres_rdb.html#pqsetdb,cstring,cstring,cstring,cstring,cstring proc pqsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn 156 +nim pqfinish query_builder/libs/postgres/postgres_rdb.html#pqfinish,PPGconn proc pqfinish(conn: PPGconn) 157 +nim pqconndefaults query_builder/libs/postgres/postgres_rdb.html#pqconndefaults proc pqconndefaults(): PPQconninfoOption 158 +nim pqconninfoFree query_builder/libs/postgres/postgres_rdb.html#pqconninfoFree,PPQconninfoOption proc pqconninfoFree(connOptions: PPQconninfoOption) 160 +nim pqresetStart query_builder/libs/postgres/postgres_rdb.html#pqresetStart,PPGconn proc pqresetStart(conn: PPGconn): int32 162 +nim pqresetPoll query_builder/libs/postgres/postgres_rdb.html#pqresetPoll,PPGconn proc pqresetPoll(conn: PPGconn): PostgresPollingStatusType 164 +nim pqreset query_builder/libs/postgres/postgres_rdb.html#pqreset,PPGconn proc pqreset(conn: PPGconn) 166 +nim pqGetCancel query_builder/libs/postgres/postgres_rdb.html#pqGetCancel,PPGconn proc pqGetCancel(conn: PPGconn): PPGcancel 170 +nim pqFreeCancel query_builder/libs/postgres/postgres_rdb.html#pqFreeCancel,PPGcancel proc pqFreeCancel(cancel: PPGcancel) 171 +nim pqCancel query_builder/libs/postgres/postgres_rdb.html#pqCancel,PPGcancel,cstring,int32 proc pqCancel(cancel: PPGcancel; errbuf: cstring; errbufsize: int32): int32 172 +nim pqrequestCancel query_builder/libs/postgres/postgres_rdb.html#pqrequestCancel,PPGconn proc pqrequestCancel(conn: PPGconn): int32 174 +nim pqdb query_builder/libs/postgres/postgres_rdb.html#pqdb,PPGconn proc pqdb(conn: PPGconn): cstring 176 +nim pquser query_builder/libs/postgres/postgres_rdb.html#pquser,PPGconn proc pquser(conn: PPGconn): cstring 177 +nim pqpass query_builder/libs/postgres/postgres_rdb.html#pqpass,PPGconn proc pqpass(conn: PPGconn): cstring 178 +nim pqhost query_builder/libs/postgres/postgres_rdb.html#pqhost,PPGconn proc pqhost(conn: PPGconn): cstring 179 +nim pqport query_builder/libs/postgres/postgres_rdb.html#pqport,PPGconn proc pqport(conn: PPGconn): cstring 180 +nim pqtty query_builder/libs/postgres/postgres_rdb.html#pqtty,PPGconn proc pqtty(conn: PPGconn): cstring 181 +nim pqoptions query_builder/libs/postgres/postgres_rdb.html#pqoptions,PPGconn proc pqoptions(conn: PPGconn): cstring 182 +nim pqstatus query_builder/libs/postgres/postgres_rdb.html#pqstatus,PPGconn proc pqstatus(conn: PPGconn): ConnStatusType 184 +nim pqtransactionStatus query_builder/libs/postgres/postgres_rdb.html#pqtransactionStatus,PPGconn proc pqtransactionStatus(conn: PPGconn): PGTransactionStatusType 186 +nim pqparameterStatus query_builder/libs/postgres/postgres_rdb.html#pqparameterStatus,PPGconn,cstring proc pqparameterStatus(conn: PPGconn; paramName: cstring): cstring 188 +nim pqserverVersion query_builder/libs/postgres/postgres_rdb.html#pqserverVersion,PPGconn proc pqserverVersion(conn: PPGconn): int32 190 +nim pqprotocolVersion query_builder/libs/postgres/postgres_rdb.html#pqprotocolVersion,PPGconn proc pqprotocolVersion(conn: PPGconn): int32 192 +nim pqerrorMessage query_builder/libs/postgres/postgres_rdb.html#pqerrorMessage,PPGconn proc pqerrorMessage(conn: PPGconn): cstring 194 +nim pqsocket query_builder/libs/postgres/postgres_rdb.html#pqsocket,PPGconn proc pqsocket(conn: PPGconn): int32 196 +nim pqbackendPID query_builder/libs/postgres/postgres_rdb.html#pqbackendPID,PPGconn proc pqbackendPID(conn: PPGconn): int32 198 +nim pqconnectionNeedsPassword query_builder/libs/postgres/postgres_rdb.html#pqconnectionNeedsPassword,PPGconn proc pqconnectionNeedsPassword(conn: PPGconn): int32 200 +nim pqconnectionUsedPassword query_builder/libs/postgres/postgres_rdb.html#pqconnectionUsedPassword,PPGconn proc pqconnectionUsedPassword(conn: PPGconn): int32 202 +nim pqclientEncoding query_builder/libs/postgres/postgres_rdb.html#pqclientEncoding,PPGconn proc pqclientEncoding(conn: PPGconn): int32 204 +nim pqsetClientEncoding query_builder/libs/postgres/postgres_rdb.html#pqsetClientEncoding,PPGconn,cstring proc pqsetClientEncoding(conn: PPGconn; encoding: cstring): int32 206 +nim pqsetErrorVerbosity query_builder/libs/postgres/postgres_rdb.html#pqsetErrorVerbosity,PPGconn,PGVerbosity proc pqsetErrorVerbosity(conn: PPGconn; verbosity: PGVerbosity): PGVerbosity 212 +nim pqtrace query_builder/libs/postgres/postgres_rdb.html#pqtrace,PPGconn,File proc pqtrace(conn: PPGconn; debug_port: File) 214 +nim pquntrace query_builder/libs/postgres/postgres_rdb.html#pquntrace,PPGconn proc pquntrace(conn: PPGconn) 216 +nim pqsetNoticeReceiver query_builder/libs/postgres/postgres_rdb.html#pqsetNoticeReceiver,PPGconn,PQnoticeReceiver,pointer proc pqsetNoticeReceiver(conn: PPGconn; theProc: PQnoticeReceiver; arg: pointer): PQnoticeReceiver 217 +nim pqsetNoticeProcessor query_builder/libs/postgres/postgres_rdb.html#pqsetNoticeProcessor,PPGconn,PQnoticeProcessor,pointer proc pqsetNoticeProcessor(conn: PPGconn; theProc: PQnoticeProcessor; arg: pointer): PQnoticeProcessor 219 +nim pqexec query_builder/libs/postgres/postgres_rdb.html#pqexec,PPGconn,cstring proc pqexec(conn: PPGconn; query: cstring): PPGresult 222 +nim pqexecParams query_builder/libs/postgres/postgres_rdb.html#pqexecParams,PPGconn,cstring,int32,POid,cstringArray,ptr.int32,ptr.int32,int32 proc pqexecParams(conn: PPGconn; command: cstring; nParams: int32; paramTypes: POid;\n paramValues: cstringArray; paramLengths, paramFormats: ptr int32;\n resultFormat: int32): PPGresult 224 +nim pqprepare query_builder/libs/postgres/postgres_rdb.html#pqprepare,PPGconn,cstring,cstring,int32,POid proc pqprepare(conn: PPGconn; stmtName, query: cstring; nParams: int32;\n paramTypes: POid): PPGresult 228 +nim pqexecPrepared query_builder/libs/postgres/postgres_rdb.html#pqexecPrepared,PPGconn,cstring,int32,cstringArray,ptr.int32,ptr.int32,int32 proc pqexecPrepared(conn: PPGconn; stmtName: cstring; nParams: int32;\n paramValues: cstringArray; paramLengths, paramFormats: ptr int32;\n resultFormat: int32): PPGresult 230 +nim pqsendQuery query_builder/libs/postgres/postgres_rdb.html#pqsendQuery,PPGconn,cstring proc pqsendQuery(conn: PPGconn; query: cstring): int32 234 +nim pqsendQueryParams query_builder/libs/postgres/postgres_rdb.html#pqsendQueryParams,PPGconn,cstring,int32,POid,cstringArray,ptr.int32,ptr.int32,int32 proc pqsendQueryParams(conn: PPGconn; command: cstring; nParams: int32;\n paramTypes: POid; paramValues: cstringArray;\n paramLengths, paramFormats: ptr int32; resultFormat: int32): int32 237 +nim pqsendPrepare query_builder/libs/postgres/postgres_rdb.html#pqsendPrepare,PPGconn,cstring,cstring,int32,POid proc pqsendPrepare(conn: PPGconn; stmtName: cstring; query: cstring; nParams: int32;\n paramTypes: POid): int32 242 +nim pqsendQueryPrepared query_builder/libs/postgres/postgres_rdb.html#pqsendQueryPrepared,PPGconn,cstring,int32,cstringArray,ptr.int32,ptr.int32,int32 proc pqsendQueryPrepared(conn: PPGconn; stmtName: cstring; nParams: int32;\n paramValues: cstringArray;\n paramLengths, paramFormats: ptr int32; resultFormat: int32): int32 245 +nim pqSetSingleRowMode query_builder/libs/postgres/postgres_rdb.html#pqSetSingleRowMode,PPGconn proc pqSetSingleRowMode(conn: PPGconn): int32 250 +nim pqgetResult query_builder/libs/postgres/postgres_rdb.html#pqgetResult,PPGconn proc pqgetResult(conn: PPGconn): PPGresult 253 +nim pqisBusy query_builder/libs/postgres/postgres_rdb.html#pqisBusy,PPGconn proc pqisBusy(conn: PPGconn): int32 255 +nim pqconsumeInput query_builder/libs/postgres/postgres_rdb.html#pqconsumeInput,PPGconn proc pqconsumeInput(conn: PPGconn): int32 257 +nim pqnotifies query_builder/libs/postgres/postgres_rdb.html#pqnotifies,PPGconn proc pqnotifies(conn: PPGconn): PPGNotify 259 +nim pqputCopyData query_builder/libs/postgres/postgres_rdb.html#pqputCopyData,PPGconn,cstring,int32 proc pqputCopyData(conn: PPGconn; buffer: cstring; nbytes: int32): int32 261 +nim pqputCopyEnd query_builder/libs/postgres/postgres_rdb.html#pqputCopyEnd,PPGconn,cstring proc pqputCopyEnd(conn: PPGconn; errormsg: cstring): int32 263 +nim pqgetCopyData query_builder/libs/postgres/postgres_rdb.html#pqgetCopyData,PPGconn,cstringArray,int32 proc pqgetCopyData(conn: PPGconn; buffer: cstringArray; async: int32): int32 265 +nim pqgetline query_builder/libs/postgres/postgres_rdb.html#pqgetline,PPGconn,cstring,int32 proc pqgetline(conn: PPGconn; str: cstring; len: int32): int32 267 +nim pqputline query_builder/libs/postgres/postgres_rdb.html#pqputline,PPGconn,cstring proc pqputline(conn: PPGconn; str: cstring): int32 269 +nim pqgetlineAsync query_builder/libs/postgres/postgres_rdb.html#pqgetlineAsync,PPGconn,cstring,int32 proc pqgetlineAsync(conn: PPGconn; buffer: cstring; bufsize: int32): int32 271 +nim pqputnbytes query_builder/libs/postgres/postgres_rdb.html#pqputnbytes,PPGconn,cstring,int32 proc pqputnbytes(conn: PPGconn; buffer: cstring; nbytes: int32): int32 273 +nim pqendcopy query_builder/libs/postgres/postgres_rdb.html#pqendcopy,PPGconn proc pqendcopy(conn: PPGconn): int32 275 +nim pqsetnonblocking query_builder/libs/postgres/postgres_rdb.html#pqsetnonblocking,PPGconn,int32 proc pqsetnonblocking(conn: PPGconn; arg: int32): int32 277 +nim pqisnonblocking query_builder/libs/postgres/postgres_rdb.html#pqisnonblocking,PPGconn proc pqisnonblocking(conn: PPGconn): int32 279 +nim pqflush query_builder/libs/postgres/postgres_rdb.html#pqflush,PPGconn proc pqflush(conn: PPGconn): int32 281 +nim pqfn query_builder/libs/postgres/postgres_rdb.html#pqfn,PPGconn,int32,ptr.int32,ptr.int32,int32,PPQArgBlock,int32 proc pqfn(conn: PPGconn; fnid: int32; result_buf, result_len: ptr int32;\n result_is_int: int32; args: PPQArgBlock; nargs: int32): PPGresult 282 +nim pqresultStatus query_builder/libs/postgres/postgres_rdb.html#pqresultStatus,PPGresult proc pqresultStatus(res: PPGresult): ExecStatusType 285 +nim pqresStatus query_builder/libs/postgres/postgres_rdb.html#pqresStatus,ExecStatusType proc pqresStatus(status: ExecStatusType): cstring 287 +nim pqresultErrorMessage query_builder/libs/postgres/postgres_rdb.html#pqresultErrorMessage,PPGresult proc pqresultErrorMessage(res: PPGresult): cstring 289 +nim pqresultErrorField query_builder/libs/postgres/postgres_rdb.html#pqresultErrorField,PPGresult,int32 proc pqresultErrorField(res: PPGresult; fieldcode: int32): cstring 291 +nim pqntuples query_builder/libs/postgres/postgres_rdb.html#pqntuples,PPGresult proc pqntuples(res: PPGresult): int32 293 +nim pqnfields query_builder/libs/postgres/postgres_rdb.html#pqnfields,PPGresult proc pqnfields(res: PPGresult): int32 295 +nim pqbinaryTuples query_builder/libs/postgres/postgres_rdb.html#pqbinaryTuples,PPGresult proc pqbinaryTuples(res: PPGresult): int32 297 +nim pqfname query_builder/libs/postgres/postgres_rdb.html#pqfname,PPGresult,int32 proc pqfname(res: PPGresult; field_num: int32): cstring 299 +nim pqfnumber query_builder/libs/postgres/postgres_rdb.html#pqfnumber,PPGresult,cstring proc pqfnumber(res: PPGresult; field_name: cstring): int32 301 +nim pqftable query_builder/libs/postgres/postgres_rdb.html#pqftable,PPGresult,int32 proc pqftable(res: PPGresult; field_num: int32): Oid 303 +nim pqftablecol query_builder/libs/postgres/postgres_rdb.html#pqftablecol,PPGresult,int32 proc pqftablecol(res: PPGresult; field_num: int32): int32 305 +nim pqfformat query_builder/libs/postgres/postgres_rdb.html#pqfformat,PPGresult,int32 proc pqfformat(res: PPGresult; field_num: int32): int32 307 +nim pqftype query_builder/libs/postgres/postgres_rdb.html#pqftype,PPGresult,int32 proc pqftype(res: PPGresult; field_num: int32): Oid 309 +nim pqfsize query_builder/libs/postgres/postgres_rdb.html#pqfsize,PPGresult,int32 proc pqfsize(res: PPGresult; field_num: int32): int32 311 +nim pqfmod query_builder/libs/postgres/postgres_rdb.html#pqfmod,PPGresult,int32 proc pqfmod(res: PPGresult; field_num: int32): int32 313 +nim pqcmdStatus query_builder/libs/postgres/postgres_rdb.html#pqcmdStatus,PPGresult proc pqcmdStatus(res: PPGresult): cstring 315 +nim pqoidStatus query_builder/libs/postgres/postgres_rdb.html#pqoidStatus,PPGresult proc pqoidStatus(res: PPGresult): cstring 317 +nim pqoidValue query_builder/libs/postgres/postgres_rdb.html#pqoidValue,PPGresult proc pqoidValue(res: PPGresult): Oid 319 +nim pqcmdTuples query_builder/libs/postgres/postgres_rdb.html#pqcmdTuples,PPGresult proc pqcmdTuples(res: PPGresult): cstring 321 +nim pqgetvalue query_builder/libs/postgres/postgres_rdb.html#pqgetvalue,PPGresult,int32,int32 proc pqgetvalue(res: PPGresult; tup_num: int32; field_num: int32): cstring 323 +nim pqgetlength query_builder/libs/postgres/postgres_rdb.html#pqgetlength,PPGresult,int32,int32 proc pqgetlength(res: PPGresult; tup_num: int32; field_num: int32): int32 325 +nim pqgetisnull query_builder/libs/postgres/postgres_rdb.html#pqgetisnull,PPGresult,int32,int32 proc pqgetisnull(res: PPGresult; tup_num: int32; field_num: int32): int32 327 +nim pqclear query_builder/libs/postgres/postgres_rdb.html#pqclear,PPGresult proc pqclear(res: PPGresult) 329 +nim pqfreemem query_builder/libs/postgres/postgres_rdb.html#pqfreemem,pointer proc pqfreemem(p: pointer) 330 +nim pqmakeEmptyPGresult query_builder/libs/postgres/postgres_rdb.html#pqmakeEmptyPGresult,PPGconn,ExecStatusType proc pqmakeEmptyPGresult(conn: PPGconn; status: ExecStatusType): PPGresult 331 +nim pqescapeString query_builder/libs/postgres/postgres_rdb.html#pqescapeString,cstring,cstring,int proc pqescapeString(till, from: cstring; len: int): int 333 +nim pqescapeBytea query_builder/libs/postgres/postgres_rdb.html#pqescapeBytea,cstring,int,int proc pqescapeBytea(bintext: cstring; binlen: int; bytealen: var int): cstring 335 +nim pqunescapeBytea query_builder/libs/postgres/postgres_rdb.html#pqunescapeBytea,cstring,int proc pqunescapeBytea(strtext: cstring; retbuflen: var int): cstring 337 +nim pqprint query_builder/libs/postgres/postgres_rdb.html#pqprint,File,PPGresult,PPQprintOpt proc pqprint(fout: File; res: PPGresult; ps: PPQprintOpt) 339 +nim pqdisplayTuples query_builder/libs/postgres/postgres_rdb.html#pqdisplayTuples,PPGresult,File,int32,cstring,int32,int32 proc pqdisplayTuples(res: PPGresult; fp: File; fillAlign: int32; fieldSep: cstring;\n printHeader: int32; quiet: int32) 341 +nim pqprintTuples query_builder/libs/postgres/postgres_rdb.html#pqprintTuples,PPGresult,File,int32,int32,int32 proc pqprintTuples(res: PPGresult; fout: File; printAttName: int32;\n terseOutput: int32; width: int32) 344 +nim lo_open query_builder/libs/postgres/postgres_rdb.html#lo_open,PPGconn,Oid,int32 proc lo_open(conn: PPGconn; lobjId: Oid; mode: int32): int32 347 +nim lo_close query_builder/libs/postgres/postgres_rdb.html#lo_close,PPGconn,int32 proc lo_close(conn: PPGconn; fd: int32): int32 349 +nim lo_read query_builder/libs/postgres/postgres_rdb.html#lo_read,PPGconn,int32,cstring,int proc lo_read(conn: PPGconn; fd: int32; buf: cstring; length: int): int32 351 +nim lo_write query_builder/libs/postgres/postgres_rdb.html#lo_write,PPGconn,int32,cstring,int proc lo_write(conn: PPGconn; fd: int32; buf: cstring; length: int): int32 353 +nim lo_lseek query_builder/libs/postgres/postgres_rdb.html#lo_lseek,PPGconn,int32,int32,int32 proc lo_lseek(conn: PPGconn; fd: int32; offset: int32; whence: int32): int32 355 +nim lo_creat query_builder/libs/postgres/postgres_rdb.html#lo_creat,PPGconn,int32 proc lo_creat(conn: PPGconn; mode: int32): Oid 357 +nim lo_tell query_builder/libs/postgres/postgres_rdb.html#lo_tell,PPGconn,int32 proc lo_tell(conn: PPGconn; fd: int32): int32 359 +nim lo_unlink query_builder/libs/postgres/postgres_rdb.html#lo_unlink,PPGconn,Oid proc lo_unlink(conn: PPGconn; lobjId: Oid): int32 361 +nim lo_import query_builder/libs/postgres/postgres_rdb.html#lo_import,PPGconn,cstring proc lo_import(conn: PPGconn; filename: cstring): Oid 363 +nim lo_export query_builder/libs/postgres/postgres_rdb.html#lo_export,PPGconn,Oid,cstring proc lo_export(conn: PPGconn; lobjId: Oid; filename: cstring): int32 365 +nim pqmblen query_builder/libs/postgres/postgres_rdb.html#pqmblen,cstring,int32 proc pqmblen(s: cstring; encoding: int32): int32 367 +nim pqenv2encoding query_builder/libs/postgres/postgres_rdb.html#pqenv2encoding proc pqenv2encoding(): int32 369 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.html b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.html new file mode 100644 index 0000000..6a677aa --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.html @@ -0,0 +1,277 @@ + + + + + + + +src/allographer/query_builder/libs/sqlite/sqlite_impl + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/sqlite/sqlite_impl

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(db: PSqlite3; query: string; args: JsonNode;
+          columns: seq[(string, string)]; timeout: int): owned(Future[void]) {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, DbError],
+    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + args is JArray + +
+
+
+
proc exec(db: PSqlite3; query: string; args: JsonNode; timeout: int): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, ValueError, DbError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + used for rdb.raw().exec() args are JArray + +
+
+
+
proc exec(db: PSqlite3; query: string; args: seq[string]; timeout: int): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, ValueError, DbError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + Not used anymore + +
+
+ +
+
+
+
proc getColumns(db: PSqlite3; query: string; args: seq[string]; timeout: int): Future[
+    seq[string]] {....stackTrace: false, raises: [Exception, ValueError, DbError],
+                   tags: [RootEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getColumnTypes(db: PSqlite3; query: string): Future[seq[(string, string)]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, DbError],
+    tags: [RootEffect, TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc prepare(db: PSqlite3; query: string; timeout: int): Future[PStmt] {.
+    ...stackTrace: false, raises: [Exception, ValueError, DbError],
+    tags: [RootEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc preparedExec(db: PSqlite3; args: seq[string] = @[]; sqliteStmt: PStmt): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, DbError, ValueError],
+                    tags: [RootEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc preparedQuery(db: PSqlite3; args: seq[string] = @[]; sqliteStmt: PStmt): Future[
+    (seq[Row], DbRows)] {....stackTrace: false,
+                          raises: [Exception, ValueError, DbError], tags: [
+    RootEffect, TimeEffect, ReadDbEffect, WriteDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc query(db: PSqlite3; query: string; args: seq[string]; timeout: int): Future[
+    (seq[Row], DbRows)] {....stackTrace: false,
+                          raises: [Exception, ValueError, DbError],
+                          tags: [RootEffect, TimeEffect, ReadDbEffect],
+                          forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc queryPlain(db: PSqlite3; query: string; args: seq[string]; timeout: int): Future[
+    seq[Row]] {....stackTrace: false, raises: [Exception, ValueError, DbError],
+                tags: [RootEffect, TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.idx b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.idx new file mode 100644 index 0000000..56a2407 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_impl.idx @@ -0,0 +1,12 @@ +nimTitle sqlite_impl query_builder/libs/sqlite/sqlite_impl.html module src/allographer/query_builder/libs/sqlite/sqlite_impl 0 +nim query query_builder/libs/sqlite/sqlite_impl.html#query,PSqlite3,string,seq[string],int proc query(db: PSqlite3; query: string; args: seq[string]; timeout: int): Future[\n (seq[Row], DbRows)] 9 +nim queryPlain query_builder/libs/sqlite/sqlite_impl.html#queryPlain,PSqlite3,string,seq[string],int proc queryPlain(db: PSqlite3; query: string; args: seq[string]; timeout: int): Future[\n seq[Row]] 22 +nim getColumnTypes query_builder/libs/sqlite/sqlite_impl.html#getColumnTypes,PSqlite3,string proc getColumnTypes(db: PSqlite3; query: string): Future[seq[(string, string)]] 34 +nim exec query_builder/libs/sqlite/sqlite_impl.html#exec,PSqlite3,string,JsonNode,seq[],int proc exec(db: PSqlite3; query: string; args: JsonNode;\n columns: seq[(string, string)]; timeout: int): owned(Future[void]) 43 +nim exec query_builder/libs/sqlite/sqlite_impl.html#exec,PSqlite3,string,JsonNode,int proc exec(db: PSqlite3; query: string; args: JsonNode; timeout: int): owned(\n Future[void]) 96 +nim exec query_builder/libs/sqlite/sqlite_impl.html#exec,PSqlite3,string,seq[string],int proc exec(db: PSqlite3; query: string; args: seq[string]; timeout: int): owned(\n Future[void]) 137 +nim getColumns query_builder/libs/sqlite/sqlite_impl.html#getColumns,PSqlite3,string,seq[string],int proc getColumns(db: PSqlite3; query: string; args: seq[string]; timeout: int): Future[\n seq[string]] 155 +nim prepare query_builder/libs/sqlite/sqlite_impl.html#prepare,PSqlite3,string,int proc prepare(db: PSqlite3; query: string; timeout: int): Future[PStmt] 161 +nim preparedQuery query_builder/libs/sqlite/sqlite_impl.html#preparedQuery,PSqlite3,seq[string],PStmt proc preparedQuery(db: PSqlite3; args: seq[string] = @[]; sqliteStmt: PStmt): Future[\n (seq[Row], DbRows)] 167 +nim preparedExec query_builder/libs/sqlite/sqlite_impl.html#preparedExec,PSqlite3,seq[string],PStmt proc preparedExec(db: PSqlite3; args: seq[string] = @[]; sqliteStmt: PStmt): owned(\n Future[void]) 185 +nimgrp exec query_builder/libs/sqlite/sqlite_impl.html#exec-procs-all proc 43 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.html b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.html new file mode 100644 index 0000000..e5eb67a --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.html @@ -0,0 +1,331 @@ + + + + + + + +src/allographer/query_builder/libs/sqlite/sqlite_lib + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/sqlite/sqlite_lib

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc `[]`(row: InstantRow; col: int32): string {.inline, ...raises: [], tags: [],
+    forbids: [].}
+
+ +

Returns text for given column of the row.

+

See also:

+ + + +
+
+ +
+
+
+
proc bindParam(ps: PStmt; paramIdx: int; val: string; copy = true) {.
+    ...raises: [DbError], tags: [], forbids: [].}
+
+ + Binds a string to the specified paramIndex. if copy is true then SQLite makes its own private copy of the data immediately + +
+
+ +
+
+
+
proc dbError(db: PSqlite3) {.noreturn, ...raises: [DbError], tags: [], forbids: [].}
+
+ +

Raises a DbError exception.

+

Examples:

+
let db = open("mytest.db", "", "", "")
+if not db.tryExec(sql"SELECT * FROM not_exist_table"):
+  dbError(db)
+db.close()
+ +
+
+ +
+
+
+
proc dbFormat(formatstr: string; args: varargs[string]): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getColumns(db: PSqlite3; dbRows: var DbRows; query: string;
+                args: seq[string]): seq[string] {....raises: [DbError, DbError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc len(row: InstantRow): int32 {.inline, ...raises: [], tags: [], forbids: [].}
+
+ +

Returns number of columns in a row.

+

See also:

+ + + +
+
+ +
+
+
+
proc sqliteQuoteIdent(name: string): string {....raises: [], tags: [], forbids: [].}
+
+ + PRAGMA 等で使う SQLite の二重引用符識別子(内部の """ にエスケープ)。 + +
+
+ +
+ +
+
+
+

Iterators

+
+
+
+
iterator instantRows(db: PSqlite3; dbRows: var DbRows; query: string;
+                     args: seq[string]): InstantRow {....tags: [ReadDbEffect],
+    raises: [DbError, DbError], forbids: [].}
+
+ +

Similar to instantRows iterator, but sets information about columns to columns.

+

Examples:

+
let db = open("mytest.db", "", "", "")
+
+# Records of my_table:
+# | id | name     |
+# |----|----------|
+# |  1 | item#1   |
+# |  2 | item#2   |
+
+var columns: DbColumns
+for row in db.instantRows(columns, sql"SELECT * FROM my_table"):
+  discard
+echo columns[0]
+
+# Output:
+# (name: "id", tableName: "my_table", typ: (kind: dbNull,
+# notNull: false, name: "INTEGER", size: 0, maxReprLen: 0, precision: 0,
+# scale: 0, min: 0, max: 0, validValues: @[]), primaryKey: false,
+# foreignKey: false)
+
+db.close()
+ +
+
+
+
iterator instantRows(db: PSqlite3; dbRows: var DbRows; sqliteStmt: PStmt): InstantRow {.
+    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}
+
+ + + +
+
+ +
+
+
+
iterator instantRowsPlain(db: PSqlite3; query: string; args: seq[string]): InstantRow {.
+    ...tags: [ReadDbEffect], raises: [DbError, DbError], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+
+

Templates

+
+
+
+
template dbBindParamError(paramIdx: int; val: varargs[untyped])
+
+ + Raises a DbError exception. + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.idx b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.idx new file mode 100644 index 0000000..0e13f63 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_lib.idx @@ -0,0 +1,13 @@ +nimTitle sqlite_lib query_builder/libs/sqlite/sqlite_lib.html module src/allographer/query_builder/libs/sqlite/sqlite_lib 0 +nim sqliteQuoteIdent query_builder/libs/sqlite/sqlite_lib.html#sqliteQuoteIdent,string proc sqliteQuoteIdent(name: string): string 23 +nim dbError query_builder/libs/sqlite/sqlite_lib.html#dbError,PSqlite3 proc dbError(db: PSqlite3) 34 +nim dbFormat query_builder/libs/sqlite/sqlite_lib.html#dbFormat,string,varargs[string] proc dbFormat(formatstr: string; args: varargs[string]): string 50 +nim instantRows query_builder/libs/sqlite/sqlite_lib.html#instantRows.i,PSqlite3,DbRows,string,seq[string] iterator instantRows(db: PSqlite3; dbRows: var DbRows; query: string; args: seq[string]): InstantRow 97 +nim instantRowsPlain query_builder/libs/sqlite/sqlite_lib.html#instantRowsPlain.i,PSqlite3,string,seq[string] iterator instantRowsPlain(db: PSqlite3; query: string; args: seq[string]): InstantRow 137 +nim instantRows query_builder/libs/sqlite/sqlite_lib.html#instantRows.i,PSqlite3,DbRows,PStmt iterator instantRows(db: PSqlite3; dbRows: var DbRows; sqliteStmt: PStmt): InstantRow 146 +nim getColumns query_builder/libs/sqlite/sqlite_lib.html#getColumns,PSqlite3,DbRows,string,seq[string] proc getColumns(db: PSqlite3; dbRows: var DbRows; query: string; args: seq[string]): seq[\n string] 159 +nim len query_builder/libs/sqlite/sqlite_lib.html#len,InstantRow proc len(row: InstantRow): int32 173 +nim `[]` query_builder/libs/sqlite/sqlite_lib.html#[],InstantRow,int32 proc `[]`(row: InstantRow; col: int32): string 181 +nim dbBindParamError query_builder/libs/sqlite/sqlite_lib.html#dbBindParamError.t,int,varargs[untyped] template dbBindParamError(paramIdx: int; val: varargs[untyped]) 189 +nim bindParam query_builder/libs/sqlite/sqlite_lib.html#bindParam,PStmt,int,string proc bindParam(ps: PStmt; paramIdx: int; val: string; copy = true) 196 +nimgrp instantrows query_builder/libs/sqlite/sqlite_lib.html#instantRows-iterators-all iterator 97 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.html b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.html new file mode 100644 index 0000000..f579736 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.html @@ -0,0 +1,2698 @@ + + + + + + + +src/allographer/query_builder/libs/sqlite/sqlite_rdb + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/sqlite/sqlite_rdb

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
Callback = proc (para1: pointer; para2: int32; para3, para4: cstringArray): int32 {.
+    cdecl.}
+
+ + + +
+
+
+
Collation_needed_func = proc (para1: pointer; para2: PSqlite3; eTextRep: int32;
+                              para4: cstring) {.cdecl.}
+
+ + + +
+
+
+
Create_collation_func = proc (para1: pointer; para2: int32; para3: pointer;
+                              para4: int32; para5: pointer): int32 {.cdecl.}
+
+ + + +
+
+
+
Create_function_final_func = proc (para1: Pcontext) {.cdecl.}
+
+ + + +
+
+
+
Create_function_func_func = proc (para1: Pcontext; para2: int32;
+                                  para3: PValueArg) {.cdecl.}
+
+ + + +
+
+
+
Create_function_step_func = proc (para1: Pcontext; para2: int32;
+                                  para3: PValueArg) {.cdecl.}
+
+ + + +
+
+
+
InstantRow = PStmt
+
+ + A handle that can be used to get a row's column + +
+
+
+
Pcontext = ptr Context
+
+ + + +
+
+
+
PPSqlite3 = ptr PSqlite3
+
+ + + +
+
+
+
PPSqlite3_Backup = ptr PSqlite3_Backup
+
+ + + +
+
+
+
PSqlite3 = ptr Sqlite3
+
+ + + +
+
+
+
PSqlite3_Backup = ptr Sqlite3_Backup
+
+ + + +
+
+
+
PStmt = ptr TStmt
+
+ + + +
+
+
+
PValue = ptr Value
+
+ + + +
+
+
+
PValueArg = array[0 .. 127, PValue]
+
+ + + +
+
+
+
Result_func = proc (para1: pointer) {.cdecl.}
+
+ + + +
+
+
+
Tbind_destructor_func = proc (para1: pointer) {.cdecl, ...tags: [], gcsafe.}
+
+ + + +
+
+ +
+
+
+

Consts

+
+
+
SQLITE_ABORT = 4
+
+ + + +
+
+
+
SQLITE_ALTER_TABLE = 26
+
+ + + +
+
+
+
SQLITE_ANY = 5
+
+ + + +
+
+
+
SQLITE_ATTACH = 24
+
+ + + +
+
+
+
SQLITE_AUTH = 23
+
+ + + +
+
+
+
SQLITE_BLOB = 4
+
+ + + +
+
+
+
SQLITE_BUSY = 5
+
+ + + +
+
+
+
SQLITE_CANTOPEN = 14
+
+ + + +
+
+
+
SQLITE_CONSTRAINT = 19
+
+ + + +
+
+
+
SQLITE_COPY = 0
+
+ + + +
+
+
+
SQLITE_CORRUPT = 11
+
+ + + +
+
+
+
SQLITE_CREATE_INDEX = 1
+
+ + + +
+
+
+
SQLITE_CREATE_TABLE = 2
+
+ + + +
+
+
+
SQLITE_CREATE_TEMP_INDEX = 3
+
+ + + +
+
+
+
SQLITE_CREATE_TEMP_TABLE = 4
+
+ + + +
+
+
+
SQLITE_CREATE_TEMP_TRIGGER = 5
+
+ + + +
+
+
+
SQLITE_CREATE_TEMP_VIEW = 6
+
+ + + +
+
+
+
SQLITE_CREATE_TRIGGER = 7
+
+ + + +
+
+
+
SQLITE_CREATE_VIEW = 8
+
+ + + +
+
+
+
SQLITE_DELETE = 9
+
+ + + +
+
+
+
SQLITE_DENY = 1
+
+ + + +
+
+
+
SQLITE_DETACH = 25
+
+ + + +
+
+
+
SQLITE_DETERMINISTIC = 0x00000800
+
+ + + +
+
+
+
SQLITE_DONE = 101
+
+ + + +
+
+
+
SQLITE_DROP_INDEX = 10
+
+ + + +
+
+
+
SQLITE_DROP_TABLE = 11
+
+ + + +
+
+
+
SQLITE_DROP_TEMP_INDEX = 12
+
+ + + +
+
+
+
SQLITE_DROP_TEMP_TABLE = 13
+
+ + + +
+
+
+
SQLITE_DROP_TEMP_TRIGGER = 14
+
+ + + +
+
+
+
SQLITE_DROP_TEMP_VIEW = 15
+
+ + + +
+
+
+
SQLITE_DROP_TRIGGER = 16
+
+ + + +
+
+
+
SQLITE_DROP_VIEW = 17
+
+ + + +
+
+
+
SQLITE_EMPTY = 16
+
+ + + +
+
+
+
SQLITE_ERROR = 1
+
+ + + +
+
+
+
SQLITE_FLOAT = 2
+
+ + + +
+
+
+
SQLITE_FORMAT = 24
+
+ + + +
+
+
+
SQLITE_FULL = 13
+
+ + + +
+
+
+
SQLITE_IGNORE = 2
+
+ + + +
+
+
+
SQLITE_INSERT = 18
+
+ + + +
+
+
+
SQLITE_INTEGER = 1
+
+ + + +
+
+
+
SQLITE_INTERNAL = 2
+
+ + + +
+
+
+
SQLITE_INTERRUPT = 9
+
+ + + +
+
+
+
SQLITE_IOERR = 10
+
+ + + +
+
+
+
SQLITE_LOCKED = 6
+
+ + + +
+
+
+
SQLITE_MISMATCH = 20
+
+ + + +
+
+
+
SQLITE_MISUSE = 21
+
+ + + +
+
+
+
SQLITE_NOLFS = 22
+
+ + + +
+
+
+
SQLITE_NOMEM = 7
+
+ + + +
+
+
+
SQLITE_NOTADB = 26
+
+ + + +
+
+
+
SQLITE_NOTFOUND = 12
+
+ + + +
+
+
+
SQLITE_NULL = 5
+
+ + + +
+
+
+
SQLITE_OK = 0
+
+ + + +
+
+
+
SQLITE_PERM = 3
+
+ + + +
+
+
+
SQLITE_PRAGMA = 19
+
+ + + +
+
+
+
SQLITE_PROTOCOL = 15
+
+ + + +
+
+
+
SQLITE_RANGE = 25
+
+ + + +
+
+
+
SQLITE_READ = 20
+
+ + + +
+
+
+
SQLITE_READONLY = 8
+
+ + + +
+
+
+
SQLITE_REINDEX = 27
+
+ + + +
+
+
+
SQLITE_ROW = 100
+
+ + + +
+
+
+
SQLITE_SCHEMA = 17
+
+ + + +
+
+
+
SQLITE_SELECT = 21
+
+ + + +
+
+
+
SQLITE_STATIC = nil
+
+ + + +
+
+
+
SQLITE_TEXT = 3
+
+ + + +
+
+
+
SQLITE_TOOBIG = 18
+
+ + + +
+
+
+
SQLITE_TRANSACTION = 22
+
+ + + +
+
+
+
SQLITE_TRANSIENT = -1
+
+ + + +
+
+
+
SQLITE_UPDATE = 23
+
+ + + +
+
+
+
SQLITE_UTF8 = 1
+
+ + + +
+
+
+
SQLITE_UTF16 = 4
+
+ + + +
+
+
+
SQLITE_UTF16BE = 3
+
+ + + +
+
+
+
SQLITE_UTF16LE = 2
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc aggregate_context(para1: Pcontext; nBytes: int32): pointer {.cdecl,
+    dynlib: Lib, importc: "sqlite3_aggregate_context", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc aggregate_count(para1: Pcontext): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_aggregate_count", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc backup_finish(pBackup: PSqlite3_Backup): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_backup_finish", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc backup_init(pDest: PSqlite3; zDestName: cstring; pSource: PSqlite3;
+                 zSourceName: cstring): PSqlite3_Backup {.cdecl, dynlib: Lib,
+    importc: "sqlite3_backup_init", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc backup_pagecount(pBackup: PSqlite3_Backup): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_backup_pagecount", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc backup_remaining(pBackup: PSqlite3_Backup): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_backup_remaining", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc backup_step(pBackup: PSqlite3_Backup; nPage: int32): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_backup_step", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_blob(para1: PStmt; para2: int32; para3: pointer; n: int32;
+               para5: Tbind_destructor_func): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_bind_blob", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc bind_blob(para1: PStmt; para2: int32; para3: pointer; n: int32;
+               para5: int32): int32 {.cdecl, dynlib: Lib,
+                                      importc: "sqlite3_bind_blob", ...raises: [],
+                                      tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_double(para1: PStmt; para2: int32; para3: float64): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_bind_double", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_int(para1: PStmt; para2: int32; para3: int32): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_bind_int", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_int64(para1: PStmt; para2: int32; para3: int64): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_bind_int64", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_null(para1: PStmt; para2: int32): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_bind_null", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_parameter_count(para1: PStmt): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_bind_parameter_count", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_parameter_index(para1: PStmt; zName: cstring): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_bind_parameter_index", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_parameter_name(para1: PStmt; para2: int32): cstring {.cdecl,
+    dynlib: Lib, importc: "sqlite3_bind_parameter_name", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_text(para1: PStmt; para2: int32; para3: cstring; n: int32;
+               para5: Tbind_destructor_func): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_bind_text", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc bind_text(para1: PStmt; para2: int32; para3: cstring; n: int32;
+               para5: int32): int32 {.cdecl, dynlib: Lib,
+                                      importc: "sqlite3_bind_text", ...raises: [],
+                                      tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bind_text16(para1: PStmt; para2: int32; para3: pointer; para4: int32;
+                 para5: int32): int32 {.cdecl, dynlib: Lib,
+                                        importc: "sqlite3_bind_text16",
+                                        ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc bind_text16(para1: PStmt; para2: int32; para3: pointer; para4: int32;
+                 para5: Tbind_destructor_func): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_bind_text16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc busy_handler(para1: PSqlite3;
+                  para2: proc (para1: pointer; para2: int32): int32 {.cdecl.};
+                  para3: pointer): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_busy_handler", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc busy_timeout(para1: PSqlite3; ms: int32): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_busy_timeout", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc changes(para1: PSqlite3): int32 {.cdecl, dynlib: Lib,
+                                       importc: "sqlite3_changes", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc clear_bindings(para1: PStmt): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_clear_bindings", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc close(para1: PSqlite3): int32 {.cdecl, dynlib: Lib,
+                                     importc: "sqlite3_close", ...raises: [],
+                                     tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc collation_needed(para1: PSqlite3; para2: pointer;
+                      para3: Collation_needed_func): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_collation_needed", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc collation_needed16(para1: PSqlite3; para2: pointer;
+                        para3: Collation_needed_func): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_collation_needed16", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_blob(para1: PStmt; iCol: int32): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_blob", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_bytes(para1: PStmt; iCol: int32): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_bytes", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_bytes16(para1: PStmt; iCol: int32): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_bytes16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_count(PStmt: PStmt): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_count", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_decltype(para1: PStmt; i: int32): cstring {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_decltype", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_decltype16(para1: PStmt; para2: int32): pointer {.cdecl,
+    dynlib: Lib, importc: "sqlite3_column_decltype16", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_double(para1: PStmt; iCol: int32): float64 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_double", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_int(para1: PStmt; iCol: int32): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_int", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_int64(para1: PStmt; iCol: int32): int64 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_int64", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_name(para1: PStmt; para2: int32): cstring {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_name", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_name16(para1: PStmt; para2: int32): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_name16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_table_name(para1: PStmt; para2: int32): cstring {.cdecl,
+    dynlib: Lib, importc: "sqlite3_column_table_name", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_text(para1: PStmt; iCol: int32): cstring {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_text", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_text16(para1: PStmt; iCol: int32): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_text16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc column_type(para1: PStmt; iCol: int32): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_column_type", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc commit_hook(para1: PSqlite3; para2: proc (para1: pointer): int32 {.cdecl.};
+                 para3: pointer): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_commit_hook", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc complete(sql: cstring): int32 {.cdecl, dynlib: Lib,
+                                     importc: "sqlite3_complete", ...raises: [],
+                                     tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc complete16(sql: pointer): int32 {.cdecl, dynlib: Lib,
+                                       importc: "sqlite3_complete16",
+                                       ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc create_collation(para1: PSqlite3; zName: cstring; eTextRep: int32;
+                      para4: pointer; xCompare: Create_collation_func): int32 {.
+    cdecl, dynlib: Lib, importc: "sqlite3_create_collation", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc create_collation16(para1: PSqlite3; zName: cstring; eTextRep: int32;
+                        para4: pointer; xCompare: Create_collation_func): int32 {.
+    cdecl, dynlib: Lib, importc: "sqlite3_create_collation16", ...raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc create_function(para1: PSqlite3; zFunctionName: cstring; nArg: int32;
+                     eTextRep: int32; para5: pointer;
+                     xFunc: Create_function_func_func;
+                     xStep: Create_function_step_func;
+                     xFinal: Create_function_final_func): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_create_function", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc create_function16(para1: PSqlite3; zFunctionName: pointer; nArg: int32;
+                       eTextRep: int32; para5: pointer;
+                       xFunc: Create_function_func_func;
+                       xStep: Create_function_step_func;
+                       xFinal: Create_function_final_func): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_create_function16", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc data_count(PStmt: PStmt): int32 {.cdecl, dynlib: Lib,
+                                       importc: "sqlite3_data_count",
+                                       ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc errcode(db: PSqlite3): int32 {.cdecl, dynlib: Lib,
+                                    importc: "sqlite3_errcode", ...raises: [],
+                                    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc errmsg(para1: PSqlite3): cstring {.cdecl, dynlib: Lib,
+                                        importc: "sqlite3_errmsg", ...raises: [],
+                                        tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc errmsg16(para1: PSqlite3): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_errmsg16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc exec(para1: PSqlite3; sql: cstring; para3: Callback; para4: pointer;
+          errmsg: var cstring): int32 {.cdecl, dynlib: Lib,
+                                        importc: "sqlite3_exec", ...raises: [],
+                                        tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc finalize(PStmt: PStmt): int32 {.cdecl, dynlib: Lib,
+                                     importc: "sqlite3_finalize", ...raises: [],
+                                     tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc free(z: cstring) {.cdecl, dynlib: Lib, importc: "sqlite3_free", ...raises: [],
+                        tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc free_table(result: cstringArray) {.cdecl, dynlib: Lib,
+                                        importc: "sqlite3_free_table",
+                                        ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc get_auxdata(para1: Pcontext; para2: int32): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_get_auxdata", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc get_table(para1: PSqlite3; sql: cstring; resultp: var cstringArray;
+               nrow, ncolumn: var cint; errmsg: ptr cstring): int32 {.cdecl,
+    dynlib: Lib, importc: "sqlite3_get_table", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc interrupt(para1: PSqlite3) {.cdecl, dynlib: Lib,
+                                  importc: "sqlite3_interrupt", ...raises: [],
+                                  tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc last_insert_rowid(para1: PSqlite3): int64 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_last_insert_rowid", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc libversion(): cstring {.cdecl, dynlib: Lib, importc: "sqlite3_libversion",
+                             ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc libversion_number(): int32 {.cdecl, dynlib: Lib,
+                                  importc: "sqlite3_libversion_number",
+                                  ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc mprintf(para1: cstring): cstring {.cdecl, varargs, dynlib: Lib,
+                                        importc: "sqlite3_mprintf", ...raises: [],
+                                        tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc open(filename: cstring; ppDb: var PSqlite3): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_open", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc open16(filename: pointer; ppDb: var PSqlite3): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_open16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc prepare(db: PSqlite3; zSql: cstring; nBytes: int32; ppStmt: var PStmt;
+             pzTail: ptr cstring): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_prepare", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc prepare16(db: PSqlite3; zSql: pointer; nBytes: int32; ppStmt: var PStmt;
+               pzTail: var pointer): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_prepare16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc prepare_v2(db: PSqlite3; zSql: cstring; nByte: cint; ppStmt: var PStmt;
+                pzTail: ptr cstring): cint {.importc: "sqlite3_prepare_v2",
+    cdecl, dynlib: Lib, ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc progress_handler(para1: PSqlite3; para2: int32;
+                      para3: proc (para1: pointer): int32 {.cdecl.};
+                      para4: pointer) {.cdecl, dynlib: Lib,
+                                        importc: "sqlite3_progress_handler",
+                                        ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc reset(PStmt: PStmt): int32 {.cdecl, dynlib: Lib, importc: "sqlite3_reset",
+                                  ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_blob(para1: Pcontext; para2: pointer; para3: int32;
+                 para4: Result_func) {.cdecl, dynlib: Lib,
+                                       importc: "sqlite3_result_blob",
+                                       ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_double(para1: Pcontext; para2: float64) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_result_double", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_error(para1: Pcontext; para2: cstring; para3: int32) {.cdecl,
+    dynlib: Lib, importc: "sqlite3_result_error", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_error16(para1: Pcontext; para2: pointer; para3: int32) {.cdecl,
+    dynlib: Lib, importc: "sqlite3_result_error16", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_int(para1: Pcontext; para2: int32) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_result_int", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_int64(para1: Pcontext; para2: int64) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_result_int64", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_null(para1: Pcontext) {.cdecl, dynlib: Lib,
+                                    importc: "sqlite3_result_null", ...raises: [],
+                                    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_text(para1: Pcontext; para2: cstring; para3: int32;
+                 para4: Result_func) {.cdecl, dynlib: Lib,
+                                       importc: "sqlite3_result_text",
+                                       ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_text16(para1: Pcontext; para2: pointer; para3: int32;
+                   para4: Result_func) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_result_text16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_text16be(para1: Pcontext; para2: pointer; para3: int32;
+                     para4: Result_func) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_result_text16be", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_text16le(para1: Pcontext; para2: pointer; para3: int32;
+                     para4: Result_func) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_result_text16le", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc result_value(para1: Pcontext; para2: PValue) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_result_value", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc set_authorizer(para1: PSqlite3; xAuth: proc (para1: pointer; para2: int32;
+    para3: cstring; para4: cstring; para5: cstring; para6: cstring): int32 {.
+    cdecl.}; pUserData: pointer): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_set_authorizer", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc set_auxdata(para1: Pcontext; para2: int32; para3: pointer;
+                 para4: proc (para1: pointer) {.cdecl.}) {.cdecl, dynlib: Lib,
+    importc: "sqlite3_set_auxdata", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc snprintf(para1: int32; para2: cstring; para3: cstring): cstring {.cdecl,
+    dynlib: Lib, varargs, importc: "sqlite3_snprintf", ...raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sqlite3_sleep(t: int64): int64 {.cdecl, dynlib: Lib,
+                                      importc: "sqlite3_sleep", ...raises: [],
+                                      tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc step(para1: PStmt): int32 {.cdecl, dynlib: Lib, importc: "sqlite3_step",
+                                 ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc total_changes(para1: PSqlite3): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_total_changes", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc trace(para1: PSqlite3;
+           xTrace: proc (para1: pointer; para2: cstring) {.cdecl.};
+           para3: pointer): pointer {.cdecl, dynlib: Lib,
+                                      importc: "sqlite3_trace", ...raises: [],
+                                      tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc user_data(para1: Pcontext): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_user_data", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_blob(para1: PValue): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_blob", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_bytes(para1: PValue): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_bytes", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_bytes16(para1: PValue): int32 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_bytes16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_double(para1: PValue): float64 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_double", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_int(para1: PValue): int32 {.cdecl, dynlib: Lib,
+                                       importc: "sqlite3_value_int", ...raises: [],
+                                       tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_int64(para1: PValue): int64 {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_int64", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_text(para1: PValue): cstring {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_text", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_text16(para1: PValue): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_text16", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_text16be(para1: PValue): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_text16be", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_text16le(para1: PValue): pointer {.cdecl, dynlib: Lib,
+    importc: "sqlite3_value_text16le", ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc value_type(para1: PValue): int32 {.cdecl, dynlib: Lib,
+                                        importc: "sqlite3_value_type",
+                                        ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc version(): cstring {.cdecl, dynlib: Lib, importc: "sqlite3_libversion",
+                          ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.idx b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.idx new file mode 100644 index 0000000..b5dcbe6 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/sqlite/sqlite_rdb.idx @@ -0,0 +1,196 @@ +nimTitle sqlite_rdb query_builder/libs/sqlite/sqlite_rdb.html module src/allographer/query_builder/libs/sqlite/sqlite_rdb 0 +nim SQLITE_INTEGER query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_INTEGER const SQLITE_INTEGER 25 +nim SQLITE_FLOAT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_FLOAT const SQLITE_FLOAT 26 +nim SQLITE_BLOB query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_BLOB const SQLITE_BLOB 27 +nim SQLITE_NULL query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_NULL const SQLITE_NULL 28 +nim SQLITE_TEXT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_TEXT const SQLITE_TEXT 29 +nim SQLITE_UTF8 query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_UTF8 const SQLITE_UTF8 30 +nim SQLITE_UTF16LE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_UTF16LE const SQLITE_UTF16LE 31 +nim SQLITE_UTF16BE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_UTF16BE const SQLITE_UTF16BE 32 +nim SQLITE_UTF16 query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_UTF16 const SQLITE_UTF16 33 +nim SQLITE_ANY query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_ANY const SQLITE_ANY 34 +nim SQLITE_OK query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_OK const SQLITE_OK 35 +nim SQLITE_ERROR query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_ERROR const SQLITE_ERROR 36 +nim SQLITE_INTERNAL query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_INTERNAL const SQLITE_INTERNAL 37 +nim SQLITE_PERM query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_PERM const SQLITE_PERM 38 +nim SQLITE_ABORT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_ABORT const SQLITE_ABORT 39 +nim SQLITE_BUSY query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_BUSY const SQLITE_BUSY 40 +nim SQLITE_LOCKED query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_LOCKED const SQLITE_LOCKED 41 +nim SQLITE_NOMEM query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_NOMEM const SQLITE_NOMEM 42 +nim SQLITE_READONLY query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_READONLY const SQLITE_READONLY 43 +nim SQLITE_INTERRUPT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_INTERRUPT const SQLITE_INTERRUPT 44 +nim SQLITE_IOERR query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_IOERR const SQLITE_IOERR 45 +nim SQLITE_CORRUPT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CORRUPT const SQLITE_CORRUPT 46 +nim SQLITE_NOTFOUND query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_NOTFOUND const SQLITE_NOTFOUND 47 +nim SQLITE_FULL query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_FULL const SQLITE_FULL 48 +nim SQLITE_CANTOPEN query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CANTOPEN const SQLITE_CANTOPEN 49 +nim SQLITE_PROTOCOL query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_PROTOCOL const SQLITE_PROTOCOL 50 +nim SQLITE_EMPTY query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_EMPTY const SQLITE_EMPTY 51 +nim SQLITE_SCHEMA query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_SCHEMA const SQLITE_SCHEMA 52 +nim SQLITE_TOOBIG query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_TOOBIG const SQLITE_TOOBIG 53 +nim SQLITE_CONSTRAINT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CONSTRAINT const SQLITE_CONSTRAINT 54 +nim SQLITE_MISMATCH query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_MISMATCH const SQLITE_MISMATCH 55 +nim SQLITE_MISUSE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_MISUSE const SQLITE_MISUSE 56 +nim SQLITE_NOLFS query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_NOLFS const SQLITE_NOLFS 57 +nim SQLITE_AUTH query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_AUTH const SQLITE_AUTH 58 +nim SQLITE_FORMAT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_FORMAT const SQLITE_FORMAT 59 +nim SQLITE_RANGE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_RANGE const SQLITE_RANGE 60 +nim SQLITE_NOTADB query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_NOTADB const SQLITE_NOTADB 61 +nim SQLITE_ROW query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_ROW const SQLITE_ROW 62 +nim SQLITE_DONE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DONE const SQLITE_DONE 63 +nim SQLITE_COPY query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_COPY const SQLITE_COPY 64 +nim SQLITE_CREATE_INDEX query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_INDEX const SQLITE_CREATE_INDEX 65 +nim SQLITE_CREATE_TABLE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_TABLE const SQLITE_CREATE_TABLE 66 +nim SQLITE_CREATE_TEMP_INDEX query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_TEMP_INDEX const SQLITE_CREATE_TEMP_INDEX 67 +nim SQLITE_CREATE_TEMP_TABLE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_TEMP_TABLE const SQLITE_CREATE_TEMP_TABLE 68 +nim SQLITE_CREATE_TEMP_TRIGGER query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_TEMP_TRIGGER const SQLITE_CREATE_TEMP_TRIGGER 69 +nim SQLITE_CREATE_TEMP_VIEW query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_TEMP_VIEW const SQLITE_CREATE_TEMP_VIEW 70 +nim SQLITE_CREATE_TRIGGER query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_TRIGGER const SQLITE_CREATE_TRIGGER 71 +nim SQLITE_CREATE_VIEW query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_CREATE_VIEW const SQLITE_CREATE_VIEW 72 +nim SQLITE_DELETE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DELETE const SQLITE_DELETE 73 +nim SQLITE_DROP_INDEX query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_INDEX const SQLITE_DROP_INDEX 74 +nim SQLITE_DROP_TABLE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_TABLE const SQLITE_DROP_TABLE 75 +nim SQLITE_DROP_TEMP_INDEX query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_TEMP_INDEX const SQLITE_DROP_TEMP_INDEX 76 +nim SQLITE_DROP_TEMP_TABLE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_TEMP_TABLE const SQLITE_DROP_TEMP_TABLE 77 +nim SQLITE_DROP_TEMP_TRIGGER query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_TEMP_TRIGGER const SQLITE_DROP_TEMP_TRIGGER 78 +nim SQLITE_DROP_TEMP_VIEW query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_TEMP_VIEW const SQLITE_DROP_TEMP_VIEW 79 +nim SQLITE_DROP_TRIGGER query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_TRIGGER const SQLITE_DROP_TRIGGER 80 +nim SQLITE_DROP_VIEW query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DROP_VIEW const SQLITE_DROP_VIEW 81 +nim SQLITE_INSERT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_INSERT const SQLITE_INSERT 82 +nim SQLITE_PRAGMA query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_PRAGMA const SQLITE_PRAGMA 83 +nim SQLITE_READ query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_READ const SQLITE_READ 84 +nim SQLITE_SELECT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_SELECT const SQLITE_SELECT 85 +nim SQLITE_TRANSACTION query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_TRANSACTION const SQLITE_TRANSACTION 86 +nim SQLITE_UPDATE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_UPDATE const SQLITE_UPDATE 87 +nim SQLITE_ATTACH query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_ATTACH const SQLITE_ATTACH 88 +nim SQLITE_DETACH query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DETACH const SQLITE_DETACH 89 +nim SQLITE_ALTER_TABLE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_ALTER_TABLE const SQLITE_ALTER_TABLE 90 +nim SQLITE_REINDEX query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_REINDEX const SQLITE_REINDEX 91 +nim SQLITE_DENY query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DENY const SQLITE_DENY 92 +nim SQLITE_IGNORE query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_IGNORE const SQLITE_IGNORE 93 +nim SQLITE_DETERMINISTIC query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_DETERMINISTIC const SQLITE_DETERMINISTIC 96 +nim PSqlite3 query_builder/libs/sqlite/sqlite_rdb.html#PSqlite3 type PSqlite3 100 +nim PPSqlite3 query_builder/libs/sqlite/sqlite_rdb.html#PPSqlite3 type PPSqlite3 101 +nim PSqlite3_Backup query_builder/libs/sqlite/sqlite_rdb.html#PSqlite3_Backup type PSqlite3_Backup 103 +nim PPSqlite3_Backup query_builder/libs/sqlite/sqlite_rdb.html#PPSqlite3_Backup type PPSqlite3_Backup 104 +nim Pcontext query_builder/libs/sqlite/sqlite_rdb.html#Pcontext type Pcontext 106 +nim PStmt query_builder/libs/sqlite/sqlite_rdb.html#PStmt type PStmt 108 +nim InstantRow query_builder/libs/sqlite/sqlite_rdb.html#InstantRow type InstantRow 109 +nim PValue query_builder/libs/sqlite/sqlite_rdb.html#PValue type PValue 111 +nim PValueArg query_builder/libs/sqlite/sqlite_rdb.html#PValueArg type PValueArg 112 +nim Callback query_builder/libs/sqlite/sqlite_rdb.html#Callback type Callback 114 +nim Tbind_destructor_func query_builder/libs/sqlite/sqlite_rdb.html#Tbind_destructor_func type Tbind_destructor_func 116 +nim Create_function_step_func query_builder/libs/sqlite/sqlite_rdb.html#Create_function_step_func type Create_function_step_func 117 +nim Create_function_func_func query_builder/libs/sqlite/sqlite_rdb.html#Create_function_func_func type Create_function_func_func 119 +nim Create_function_final_func query_builder/libs/sqlite/sqlite_rdb.html#Create_function_final_func type Create_function_final_func 121 +nim Result_func query_builder/libs/sqlite/sqlite_rdb.html#Result_func type Result_func 122 +nim Create_collation_func query_builder/libs/sqlite/sqlite_rdb.html#Create_collation_func type Create_collation_func 123 +nim Collation_needed_func query_builder/libs/sqlite/sqlite_rdb.html#Collation_needed_func type Collation_needed_func 125 +nim SQLITE_STATIC query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_STATIC const SQLITE_STATIC 129 +nim SQLITE_TRANSIENT query_builder/libs/sqlite/sqlite_rdb.html#SQLITE_TRANSIENT const SQLITE_TRANSIENT 130 +nim close query_builder/libs/sqlite/sqlite_rdb.html#close,PSqlite3 proc close(para1: PSqlite3): int32 132 +nim exec query_builder/libs/sqlite/sqlite_rdb.html#exec,PSqlite3,cstring,Callback,pointer,cstring proc exec(para1: PSqlite3; sql: cstring; para3: Callback; para4: pointer;\n errmsg: var cstring): int32 133 +nim last_insert_rowid query_builder/libs/sqlite/sqlite_rdb.html#last_insert_rowid,PSqlite3 proc last_insert_rowid(para1: PSqlite3): int64 136 +nim changes query_builder/libs/sqlite/sqlite_rdb.html#changes,PSqlite3 proc changes(para1: PSqlite3): int32 138 +nim total_changes query_builder/libs/sqlite/sqlite_rdb.html#total_changes,PSqlite3 proc total_changes(para1: PSqlite3): int32 139 +nim interrupt query_builder/libs/sqlite/sqlite_rdb.html#interrupt,PSqlite3 proc interrupt(para1: PSqlite3) 141 +nim complete query_builder/libs/sqlite/sqlite_rdb.html#complete,cstring proc complete(sql: cstring): int32 142 +nim complete16 query_builder/libs/sqlite/sqlite_rdb.html#complete16,pointer proc complete16(sql: pointer): int32 144 +nim busy_handler query_builder/libs/sqlite/sqlite_rdb.html#busy_handler,PSqlite3,proc(pointer,int32),pointer proc busy_handler(para1: PSqlite3;\n para2: proc (para1: pointer; para2: int32): int32 {.cdecl.};\n para3: pointer): int32 146 +nim busy_timeout query_builder/libs/sqlite/sqlite_rdb.html#busy_timeout,PSqlite3,int32 proc busy_timeout(para1: PSqlite3; ms: int32): int32 150 +nim get_table query_builder/libs/sqlite/sqlite_rdb.html#get_table,PSqlite3,cstring,cstringArray,cint,cint,ptr.cstring proc get_table(para1: PSqlite3; sql: cstring; resultp: var cstringArray;\n nrow, ncolumn: var cint; errmsg: ptr cstring): int32 152 +nim free_table query_builder/libs/sqlite/sqlite_rdb.html#free_table,cstringArray proc free_table(result: cstringArray) 155 +nim mprintf query_builder/libs/sqlite/sqlite_rdb.html#mprintf,cstring proc mprintf(para1: cstring): cstring 159 +nim free query_builder/libs/sqlite/sqlite_rdb.html#free,cstring proc free(z: cstring) 162 +nim snprintf query_builder/libs/sqlite/sqlite_rdb.html#snprintf,int32,cstring,cstring proc snprintf(para1: int32; para2: cstring; para3: cstring): cstring 164 +nim set_authorizer query_builder/libs/sqlite/sqlite_rdb.html#set_authorizer,PSqlite3,proc(pointer,int32,cstring,cstring,cstring,cstring),pointer proc set_authorizer(para1: PSqlite3; xAuth: proc (para1: pointer; para2: int32;\n para3: cstring; para4: cstring; para5: cstring; para6: cstring): int32 {.\n cdecl.}; pUserData: pointer): int32 166 +nim trace query_builder/libs/sqlite/sqlite_rdb.html#trace,PSqlite3,proc(pointer,cstring),pointer proc trace(para1: PSqlite3; xTrace: proc (para1: pointer; para2: cstring) {.cdecl.};\n para3: pointer): pointer 170 +nim progress_handler query_builder/libs/sqlite/sqlite_rdb.html#progress_handler,PSqlite3,int32,proc(pointer),pointer proc progress_handler(para1: PSqlite3; para2: int32;\n para3: proc (para1: pointer): int32 {.cdecl.}; para4: pointer) 173 +nim commit_hook query_builder/libs/sqlite/sqlite_rdb.html#commit_hook,PSqlite3,proc(pointer),pointer proc commit_hook(para1: PSqlite3; para2: proc (para1: pointer): int32 {.cdecl.};\n para3: pointer): pointer 177 +nim open query_builder/libs/sqlite/sqlite_rdb.html#open,cstring,PSqlite3 proc open(filename: cstring; ppDb: var PSqlite3): int32 180 +nim open16 query_builder/libs/sqlite/sqlite_rdb.html#open16,pointer,PSqlite3 proc open16(filename: pointer; ppDb: var PSqlite3): int32 182 +nim errcode query_builder/libs/sqlite/sqlite_rdb.html#errcode,PSqlite3 proc errcode(db: PSqlite3): int32 184 +nim errmsg query_builder/libs/sqlite/sqlite_rdb.html#errmsg,PSqlite3 proc errmsg(para1: PSqlite3): cstring 185 +nim errmsg16 query_builder/libs/sqlite/sqlite_rdb.html#errmsg16,PSqlite3 proc errmsg16(para1: PSqlite3): pointer 186 +nim prepare query_builder/libs/sqlite/sqlite_rdb.html#prepare,PSqlite3,cstring,int32,PStmt,ptr.cstring proc prepare(db: PSqlite3; zSql: cstring; nBytes: int32; ppStmt: var PStmt;\n pzTail: ptr cstring): int32 188 +nim prepare_v2 query_builder/libs/sqlite/sqlite_rdb.html#prepare_v2,PSqlite3,cstring,cint,PStmt,ptr.cstring proc prepare_v2(db: PSqlite3; zSql: cstring; nByte: cint; ppStmt: var PStmt;\n pzTail: ptr cstring): cint 192 +nim prepare16 query_builder/libs/sqlite/sqlite_rdb.html#prepare16,PSqlite3,pointer,int32,PStmt,pointer proc prepare16(db: PSqlite3; zSql: pointer; nBytes: int32; ppStmt: var PStmt;\n pzTail: var pointer): int32 196 +nim bind_blob query_builder/libs/sqlite/sqlite_rdb.html#bind_blob,PStmt,int32,pointer,int32,Tbind_destructor_func proc bind_blob(para1: PStmt; para2: int32; para3: pointer; n: int32;\n para5: Tbind_destructor_func): int32 199 +nim bind_double query_builder/libs/sqlite/sqlite_rdb.html#bind_double,PStmt,int32,float64 proc bind_double(para1: PStmt; para2: int32; para3: float64): int32 202 +nim bind_int query_builder/libs/sqlite/sqlite_rdb.html#bind_int,PStmt,int32,int32 proc bind_int(para1: PStmt; para2: int32; para3: int32): int32 204 +nim bind_int64 query_builder/libs/sqlite/sqlite_rdb.html#bind_int64,PStmt,int32,int64 proc bind_int64(para1: PStmt; para2: int32; para3: int64): int32 206 +nim bind_null query_builder/libs/sqlite/sqlite_rdb.html#bind_null,PStmt,int32 proc bind_null(para1: PStmt; para2: int32): int32 208 +nim bind_text query_builder/libs/sqlite/sqlite_rdb.html#bind_text,PStmt,int32,cstring,int32,Tbind_destructor_func proc bind_text(para1: PStmt; para2: int32; para3: cstring; n: int32;\n para5: Tbind_destructor_func): int32 210 +nim bind_text16 query_builder/libs/sqlite/sqlite_rdb.html#bind_text16,PStmt,int32,pointer,int32,Tbind_destructor_func proc bind_text16(para1: PStmt; para2: int32; para3: pointer; para4: int32;\n para5: Tbind_destructor_func): int32 213 +nim bind_blob query_builder/libs/sqlite/sqlite_rdb.html#bind_blob,PStmt,int32,pointer,int32,int32 proc bind_blob(para1: PStmt; para2: int32; para3: pointer; n: int32; para5: int32): int32 219 +nim bind_text query_builder/libs/sqlite/sqlite_rdb.html#bind_text,PStmt,int32,cstring,int32,int32 proc bind_text(para1: PStmt; para2: int32; para3: cstring; n: int32; para5: int32): int32 222 +nim bind_text16 query_builder/libs/sqlite/sqlite_rdb.html#bind_text16,PStmt,int32,pointer,int32,int32 proc bind_text16(para1: PStmt; para2: int32; para3: pointer; para4: int32;\n para5: int32): int32 225 +nim bind_parameter_count query_builder/libs/sqlite/sqlite_rdb.html#bind_parameter_count,PStmt proc bind_parameter_count(para1: PStmt): int32 228 +nim bind_parameter_name query_builder/libs/sqlite/sqlite_rdb.html#bind_parameter_name,PStmt,int32 proc bind_parameter_name(para1: PStmt; para2: int32): cstring 230 +nim bind_parameter_index query_builder/libs/sqlite/sqlite_rdb.html#bind_parameter_index,PStmt,cstring proc bind_parameter_index(para1: PStmt; zName: cstring): int32 232 +nim clear_bindings query_builder/libs/sqlite/sqlite_rdb.html#clear_bindings,PStmt proc clear_bindings(para1: PStmt): int32 234 +nim column_count query_builder/libs/sqlite/sqlite_rdb.html#column_count,PStmt proc column_count(PStmt: PStmt): int32 236 +nim column_name query_builder/libs/sqlite/sqlite_rdb.html#column_name,PStmt,int32 proc column_name(para1: PStmt; para2: int32): cstring 238 +nim column_table_name query_builder/libs/sqlite/sqlite_rdb.html#column_table_name,PStmt,int32 proc column_table_name(para1: PStmt; para2: int32): cstring 240 +nim column_name16 query_builder/libs/sqlite/sqlite_rdb.html#column_name16,PStmt,int32 proc column_name16(para1: PStmt; para2: int32): pointer 242 +nim column_decltype query_builder/libs/sqlite/sqlite_rdb.html#column_decltype,PStmt,int32 proc column_decltype(para1: PStmt; i: int32): cstring 244 +nim column_decltype16 query_builder/libs/sqlite/sqlite_rdb.html#column_decltype16,PStmt,int32 proc column_decltype16(para1: PStmt; para2: int32): pointer 246 +nim step query_builder/libs/sqlite/sqlite_rdb.html#step,PStmt proc step(para1: PStmt): int32 248 +nim data_count query_builder/libs/sqlite/sqlite_rdb.html#data_count,PStmt proc data_count(PStmt: PStmt): int32 249 +nim column_blob query_builder/libs/sqlite/sqlite_rdb.html#column_blob,PStmt,int32 proc column_blob(para1: PStmt; iCol: int32): pointer 251 +nim column_bytes query_builder/libs/sqlite/sqlite_rdb.html#column_bytes,PStmt,int32 proc column_bytes(para1: PStmt; iCol: int32): int32 253 +nim column_bytes16 query_builder/libs/sqlite/sqlite_rdb.html#column_bytes16,PStmt,int32 proc column_bytes16(para1: PStmt; iCol: int32): int32 255 +nim column_double query_builder/libs/sqlite/sqlite_rdb.html#column_double,PStmt,int32 proc column_double(para1: PStmt; iCol: int32): float64 257 +nim column_int query_builder/libs/sqlite/sqlite_rdb.html#column_int,PStmt,int32 proc column_int(para1: PStmt; iCol: int32): int32 259 +nim column_int64 query_builder/libs/sqlite/sqlite_rdb.html#column_int64,PStmt,int32 proc column_int64(para1: PStmt; iCol: int32): int64 261 +nim column_text query_builder/libs/sqlite/sqlite_rdb.html#column_text,PStmt,int32 proc column_text(para1: PStmt; iCol: int32): cstring 263 +nim column_text16 query_builder/libs/sqlite/sqlite_rdb.html#column_text16,PStmt,int32 proc column_text16(para1: PStmt; iCol: int32): pointer 265 +nim column_type query_builder/libs/sqlite/sqlite_rdb.html#column_type,PStmt,int32 proc column_type(para1: PStmt; iCol: int32): int32 267 +nim finalize query_builder/libs/sqlite/sqlite_rdb.html#finalize,PStmt proc finalize(PStmt: PStmt): int32 269 +nim reset query_builder/libs/sqlite/sqlite_rdb.html#reset,PStmt proc reset(PStmt: PStmt): int32 271 +nim create_function query_builder/libs/sqlite/sqlite_rdb.html#create_function,PSqlite3,cstring,int32,int32,pointer,Create_function_func_func,Create_function_step_func,Create_function_final_func proc create_function(para1: PSqlite3; zFunctionName: cstring; nArg: int32;\n eTextRep: int32; para5: pointer;\n xFunc: Create_function_func_func;\n xStep: Create_function_step_func;\n xFinal: Create_function_final_func): int32 272 +nim create_function16 query_builder/libs/sqlite/sqlite_rdb.html#create_function16,PSqlite3,pointer,int32,int32,pointer,Create_function_func_func,Create_function_step_func,Create_function_final_func proc create_function16(para1: PSqlite3; zFunctionName: pointer; nArg: int32;\n eTextRep: int32; para5: pointer;\n xFunc: Create_function_func_func;\n xStep: Create_function_step_func;\n xFinal: Create_function_final_func): int32 278 +nim aggregate_count query_builder/libs/sqlite/sqlite_rdb.html#aggregate_count,Pcontext proc aggregate_count(para1: Pcontext): int32 284 +nim value_blob query_builder/libs/sqlite/sqlite_rdb.html#value_blob,PValue proc value_blob(para1: PValue): pointer 286 +nim value_bytes query_builder/libs/sqlite/sqlite_rdb.html#value_bytes,PValue proc value_bytes(para1: PValue): int32 288 +nim value_bytes16 query_builder/libs/sqlite/sqlite_rdb.html#value_bytes16,PValue proc value_bytes16(para1: PValue): int32 290 +nim value_double query_builder/libs/sqlite/sqlite_rdb.html#value_double,PValue proc value_double(para1: PValue): float64 292 +nim value_int query_builder/libs/sqlite/sqlite_rdb.html#value_int,PValue proc value_int(para1: PValue): int32 294 +nim value_int64 query_builder/libs/sqlite/sqlite_rdb.html#value_int64,PValue proc value_int64(para1: PValue): int64 296 +nim value_text query_builder/libs/sqlite/sqlite_rdb.html#value_text,PValue proc value_text(para1: PValue): cstring 298 +nim value_text16 query_builder/libs/sqlite/sqlite_rdb.html#value_text16,PValue proc value_text16(para1: PValue): pointer 300 +nim value_text16le query_builder/libs/sqlite/sqlite_rdb.html#value_text16le,PValue proc value_text16le(para1: PValue): pointer 302 +nim value_text16be query_builder/libs/sqlite/sqlite_rdb.html#value_text16be,PValue proc value_text16be(para1: PValue): pointer 304 +nim value_type query_builder/libs/sqlite/sqlite_rdb.html#value_type,PValue proc value_type(para1: PValue): int32 306 +nim aggregate_context query_builder/libs/sqlite/sqlite_rdb.html#aggregate_context,Pcontext,int32 proc aggregate_context(para1: Pcontext; nBytes: int32): pointer 308 +nim user_data query_builder/libs/sqlite/sqlite_rdb.html#user_data,Pcontext proc user_data(para1: Pcontext): pointer 310 +nim get_auxdata query_builder/libs/sqlite/sqlite_rdb.html#get_auxdata,Pcontext,int32 proc get_auxdata(para1: Pcontext; para2: int32): pointer 312 +nim set_auxdata query_builder/libs/sqlite/sqlite_rdb.html#set_auxdata,Pcontext,int32,pointer,proc(pointer) proc set_auxdata(para1: Pcontext; para2: int32; para3: pointer;\n para4: proc (para1: pointer) {.cdecl.}) 314 +nim result_blob query_builder/libs/sqlite/sqlite_rdb.html#result_blob,Pcontext,pointer,int32,Result_func proc result_blob(para1: Pcontext; para2: pointer; para3: int32; para4: Result_func) 317 +nim result_double query_builder/libs/sqlite/sqlite_rdb.html#result_double,Pcontext,float64 proc result_double(para1: Pcontext; para2: float64) 320 +nim result_error query_builder/libs/sqlite/sqlite_rdb.html#result_error,Pcontext,cstring,int32 proc result_error(para1: Pcontext; para2: cstring; para3: int32) 322 +nim result_error16 query_builder/libs/sqlite/sqlite_rdb.html#result_error16,Pcontext,pointer,int32 proc result_error16(para1: Pcontext; para2: pointer; para3: int32) 324 +nim result_int query_builder/libs/sqlite/sqlite_rdb.html#result_int,Pcontext,int32 proc result_int(para1: Pcontext; para2: int32) 326 +nim result_int64 query_builder/libs/sqlite/sqlite_rdb.html#result_int64,Pcontext,int64 proc result_int64(para1: Pcontext; para2: int64) 328 +nim result_null query_builder/libs/sqlite/sqlite_rdb.html#result_null,Pcontext proc result_null(para1: Pcontext) 330 +nim result_text query_builder/libs/sqlite/sqlite_rdb.html#result_text,Pcontext,cstring,int32,Result_func proc result_text(para1: Pcontext; para2: cstring; para3: int32; para4: Result_func) 332 +nim result_text16 query_builder/libs/sqlite/sqlite_rdb.html#result_text16,Pcontext,pointer,int32,Result_func proc result_text16(para1: Pcontext; para2: pointer; para3: int32; para4: Result_func) 335 +nim result_text16le query_builder/libs/sqlite/sqlite_rdb.html#result_text16le,Pcontext,pointer,int32,Result_func proc result_text16le(para1: Pcontext; para2: pointer; para3: int32;\n para4: Result_func) 338 +nim result_text16be query_builder/libs/sqlite/sqlite_rdb.html#result_text16be,Pcontext,pointer,int32,Result_func proc result_text16be(para1: Pcontext; para2: pointer; para3: int32;\n para4: Result_func) 341 +nim result_value query_builder/libs/sqlite/sqlite_rdb.html#result_value,Pcontext,PValue proc result_value(para1: Pcontext; para2: PValue) 344 +nim create_collation query_builder/libs/sqlite/sqlite_rdb.html#create_collation,PSqlite3,cstring,int32,pointer,Create_collation_func proc create_collation(para1: PSqlite3; zName: cstring; eTextRep: int32;\n para4: pointer; xCompare: Create_collation_func): int32 346 +nim create_collation16 query_builder/libs/sqlite/sqlite_rdb.html#create_collation16,PSqlite3,cstring,int32,pointer,Create_collation_func proc create_collation16(para1: PSqlite3; zName: cstring; eTextRep: int32;\n para4: pointer; xCompare: Create_collation_func): int32 349 +nim collation_needed query_builder/libs/sqlite/sqlite_rdb.html#collation_needed,PSqlite3,pointer,Collation_needed_func proc collation_needed(para1: PSqlite3; para2: pointer; para3: Collation_needed_func): int32 352 +nim collation_needed16 query_builder/libs/sqlite/sqlite_rdb.html#collation_needed16,PSqlite3,pointer,Collation_needed_func proc collation_needed16(para1: PSqlite3; para2: pointer; para3: Collation_needed_func): int32 354 +nim libversion query_builder/libs/sqlite/sqlite_rdb.html#libversion proc libversion(): cstring 356 +nim version query_builder/libs/sqlite/sqlite_rdb.html#version proc version(): cstring 358 +nim libversion_number query_builder/libs/sqlite/sqlite_rdb.html#libversion_number proc libversion_number(): int32 360 +nim backup_init query_builder/libs/sqlite/sqlite_rdb.html#backup_init,PSqlite3,cstring,PSqlite3,cstring proc backup_init(pDest: PSqlite3; zDestName: cstring; pSource: PSqlite3;\n zSourceName: cstring): PSqlite3_Backup 363 +nim backup_step query_builder/libs/sqlite/sqlite_rdb.html#backup_step,PSqlite3_Backup,int32 proc backup_step(pBackup: PSqlite3_Backup; nPage: int32): int32 366 +nim backup_finish query_builder/libs/sqlite/sqlite_rdb.html#backup_finish,PSqlite3_Backup proc backup_finish(pBackup: PSqlite3_Backup): int32 368 +nim backup_pagecount query_builder/libs/sqlite/sqlite_rdb.html#backup_pagecount,PSqlite3_Backup proc backup_pagecount(pBackup: PSqlite3_Backup): int32 370 +nim backup_remaining query_builder/libs/sqlite/sqlite_rdb.html#backup_remaining,PSqlite3_Backup proc backup_remaining(pBackup: PSqlite3_Backup): int32 372 +nim sqlite3_sleep query_builder/libs/sqlite/sqlite_rdb.html#sqlite3_sleep,int64 proc sqlite3_sleep(t: int64): int64 374 +nimgrp bindtext16 query_builder/libs/sqlite/sqlite_rdb.html#bind_text16-procs-all proc 213 +nimgrp bindtext query_builder/libs/sqlite/sqlite_rdb.html#bind_text-procs-all proc 210 +nimgrp bindblob query_builder/libs/sqlite/sqlite_rdb.html#bind_blob-procs-all proc 199 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.html b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.html new file mode 100644 index 0000000..f14124f --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.html @@ -0,0 +1,230 @@ + + + + + + + +src/allographer/query_builder/libs/surreal/surreal_impl + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/surreal/surreal_impl

+
+ +
+ +
+ +

+ +
+

Types

+
+
+
SurrealImpl = ref object
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc exec(db: SurrealConn; query: string; args: JsonNode; timeout: int): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, ValueError, DbError,
+    HttpRequestError, LibraryError, SslError, OSError, IOError, ProtocolError,
+    KeyError], tags: [RootEffect, TimeEffect, ReadIOEffect, WriteIOEffect],
+                    forbids: [].}
+
+ + + +
+
+
+
proc exec(db: SurrealConn; query: string; args: seq[string]; timeout: int): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, ValueError, DbError,
+    HttpRequestError, LibraryError, SslError, OSError, IOError, ProtocolError,
+    KeyError], tags: [RootEffect, TimeEffect, ReadIOEffect, WriteIOEffect],
+                    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc info(db: SurrealConn; query: string; args: JsonNode; timeout: int): Future[
+    JsonNode] {....stackTrace: false, raises: [Exception, ValueError, DbError,
+    HttpRequestError, LibraryError, SslError, OSError, IOError, ProtocolError,
+    KeyError], tags: [RootEffect, TimeEffect, ReadIOEffect, WriteIOEffect],
+                forbids: [].}
+
+ + + +
+
+
+
proc info(db: SurrealConn; query: string; args: seq[string]; timeout: int): Future[
+    JsonNode] {....stackTrace: false, raises: [Exception, ValueError, DbError,
+    HttpRequestError, LibraryError, SslError, OSError, IOError, ProtocolError,
+    KeyError], tags: [RootEffect, TimeEffect, ReadIOEffect, WriteIOEffect],
+                forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc query(db: SurrealConn; query: string; args: JsonNode; timeout: int): Future[
+    JsonNode] {....stackTrace: false, raises: [Exception, ValueError, DbError,
+    HttpRequestError, LibraryError, SslError, OSError, IOError, ProtocolError,
+    KeyError], tags: [RootEffect, TimeEffect, ReadIOEffect, WriteIOEffect],
+                forbids: [].}
+
+ + return JArray + +
+
+
+
proc query(db: SurrealConn; query: string; args: seq[string]; timeout: int): Future[
+    JsonNode] {....stackTrace: false, raises: [Exception, ValueError, DbError,
+    HttpRequestError, LibraryError, SslError, OSError, IOError, ProtocolError,
+    KeyError], tags: [RootEffect, TimeEffect, ReadIOEffect, WriteIOEffect],
+                forbids: [].}
+
+ + return JArray + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.idx b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.idx new file mode 100644 index 0000000..b2a8e69 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_impl.idx @@ -0,0 +1,11 @@ +nimTitle surreal_impl query_builder/libs/surreal/surreal_impl.html module src/allographer/query_builder/libs/surreal/surreal_impl 0 +nim SurrealImpl query_builder/libs/surreal/surreal_impl.html#SurrealImpl type SurrealImpl 10 +nim query query_builder/libs/surreal/surreal_impl.html#query,SurrealConn,string,seq[string],int proc query(db: SurrealConn; query: string; args: seq[string]; timeout: int): Future[\n JsonNode] 109 +nim query query_builder/libs/surreal/surreal_impl.html#query,SurrealConn,string,JsonNode,int proc query(db: SurrealConn; query: string; args: JsonNode; timeout: int): Future[\n JsonNode] 118 +nim exec query_builder/libs/surreal/surreal_impl.html#exec,SurrealConn,string,seq[string],int proc exec(db: SurrealConn; query: string; args: seq[string]; timeout: int): owned(\n Future[void]) 127 +nim exec query_builder/libs/surreal/surreal_impl.html#exec,SurrealConn,string,JsonNode,int proc exec(db: SurrealConn; query: string; args: JsonNode; timeout: int): owned(\n Future[void]) 134 +nim info query_builder/libs/surreal/surreal_impl.html#info,SurrealConn,string,seq[string],int proc info(db: SurrealConn; query: string; args: seq[string]; timeout: int): Future[\n JsonNode] 141 +nim info query_builder/libs/surreal/surreal_impl.html#info,SurrealConn,string,JsonNode,int proc info(db: SurrealConn; query: string; args: JsonNode; timeout: int): Future[\n JsonNode] 146 +nimgrp info query_builder/libs/surreal/surreal_impl.html#info-procs-all proc 141 +nimgrp query query_builder/libs/surreal/surreal_impl.html#query-procs-all proc 109 +nimgrp exec query_builder/libs/surreal/surreal_impl.html#exec-procs-all proc 127 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.html b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.html new file mode 100644 index 0000000..00ed7d7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.html @@ -0,0 +1,148 @@ + + + + + + + +src/allographer/query_builder/libs/surreal/surreal_lib + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/surreal/surreal_lib

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Procs

+
+
+
+
proc dbFormat(formatstr: string; args: varargs[string]): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+
+
proc dbFormat(queryString: string; args: JsonNode): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc numToAlphabet(n: int): string {....raises: [], tags: [], forbids: [].}
+
+ + 1 => "a", 26 => "z", 27 => "aa", 28 => "ab", 52 => "az", 53 => "ba" + +
+
+ +
+
+
+
proc questionToDaller(s: string): string {....raises: [], tags: [], forbids: [].}
+
+ +

from UPDATE user SET name = ?, email = ? WHERE id = ?

+

to UPDATE user SET name = $a, email = $b WHERE id = $c

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.idx b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.idx new file mode 100644 index 0000000..3504cbe --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_lib.idx @@ -0,0 +1,6 @@ +nimTitle surreal_lib query_builder/libs/surreal/surreal_lib.html module src/allographer/query_builder/libs/surreal/surreal_lib 0 +nim dbFormat query_builder/libs/surreal/surreal_lib.html#dbFormat,string,varargs[string] proc dbFormat(formatstr: string; args: varargs[string]): string 97 +nim numToAlphabet query_builder/libs/surreal/surreal_lib.html#numToAlphabet,int proc numToAlphabet(n: int): string 112 +nim questionToDaller query_builder/libs/surreal/surreal_lib.html#questionToDaller,string proc questionToDaller(s: string): string 118 +nim dbFormat query_builder/libs/surreal/surreal_lib.html#dbFormat,string,JsonNode proc dbFormat(queryString: string; args: JsonNode): string 169 +nimgrp dbformat query_builder/libs/surreal/surreal_lib.html#dbFormat-procs-all proc 97 diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.html b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.html new file mode 100644 index 0000000..a009eb2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.html @@ -0,0 +1,105 @@ + + + + + + + +src/allographer/query_builder/libs/surreal/surreal_rdb + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/libs/surreal/surreal_rdb

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
SurrealConn = object
+  client*: AsyncHttpClient
+  host*: string
+  port*: int32
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.idx b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.idx new file mode 100644 index 0000000..64b8405 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/libs/surreal/surreal_rdb.idx @@ -0,0 +1,2 @@ +nimTitle surreal_rdb query_builder/libs/surreal/surreal_rdb.html module src/allographer/query_builder/libs/surreal/surreal_rdb 0 +nim SurrealConn query_builder/libs/surreal/surreal_rdb.html#SurrealConn object SurrealConn 3 diff --git a/clients/nim-allographer/docs/v2/query_builder/log.html b/clients/nim-allographer/docs/v2/query_builder/log.html new file mode 100644 index 0000000..1fd8ec3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/log.html @@ -0,0 +1,168 @@ + + + + + + + +src/allographer/query_builder/log + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/log

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
LogSetting = ref object
+  shouldDisplayLog*: bool
+  shouldOutputLogFile*: bool
+  logDir*: string
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc echoErrorMsg(self: LogSetting; msg: string; args: seq[string] = @[]) {.
+    ...raises: [IOError, OSError, Exception], tags: [WriteIOEffect, WriteDirEffect,
+    ReadDirEffect, ReadIOEffect, RootEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc echoWarningMsg(self: LogSetting; msg: string) {.
+    ...raises: [IOError, OSError, Exception], tags: [WriteIOEffect, WriteDirEffect,
+    ReadDirEffect, ReadIOEffect, RootEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc logger(self: LogSetting; output: auto)
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/log.idx b/clients/nim-allographer/docs/v2/query_builder/log.idx new file mode 100644 index 0000000..c993c67 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/log.idx @@ -0,0 +1,5 @@ +nimTitle log query_builder/log.html module src/allographer/query_builder/log 0 +nim LogSetting query_builder/log.html#LogSetting type LogSetting 10 +nim logger query_builder/log.html#logger,LogSetting,auto proc logger(self: LogSetting; output: auto) 15 +nim echoErrorMsg query_builder/log.html#echoErrorMsg,LogSetting,string,seq[string] proc echoErrorMsg(self: LogSetting; msg: string; args: seq[string] = @[]) 33 +nim echoWarningMsg query_builder/log.html#echoWarningMsg,LogSetting,string proc echoWarningMsg(self: LogSetting; msg: string) 47 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/database_types.html b/clients/nim-allographer/docs/v2/query_builder/models/database_types.html new file mode 100644 index 0000000..75e1cd1 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/database_types.html @@ -0,0 +1,304 @@ + + + + + + + +src/allographer/query_builder/models/database_types + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/database_types

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
DbColumn = object
+  name*: string              ## name of the column
+  tableName*: string         ## name of the table the column belongs to (optional)
+  typ*: DbType               ## type of the column
+  primaryKey*: bool          ## is this a primary key?
+  foreignKey*: bool          ## is this a foreign key?
+
+ + information about a database column + +
+
+
+
DbColumns = seq[DbColumn]
+
+ + + +
+
+
+
DbEffect = object of IOEffect
+
+ + effect that denotes a database operation + +
+
+
+
DbRows = seq[DbColumns]
+
+ + + +
+
+
+
DbType = object
+  kind*: DbTypeKind          ## the kind of the described type
+  notNull*: bool             ## does the type contain NULL?
+  name*: string              ## the name of the type
+  size*: Natural             ## the size of the datatype; 0 if of variable size
+  maxReprLen*: Natural       ## maximal length required for the representation
+  precision*, scale*: Natural ## precision and scale of the number
+  min*, max*: BiggestInt     ## the minimum and maximum of allowed values
+  validValues*: seq[string]  ## valid values of an enum or a set
+
+ + describes a database type + +
+
+
+
DbTypeKind = enum
+  dbUnknown,                ## unknown datatype
+  dbSerial,                 ## datatype used for primary auto-increment keys
+  dbNull,                   ## datatype used for the NULL value
+  dbBit,                    ## bit datatype
+  dbBool,                   ## boolean datatype
+  dbBlob,                   ## blob datatype
+  dbFixedChar,              ## string of fixed length
+  dbVarchar,                ## string datatype
+  dbJson,                   ## JSON datatype
+  dbXml,                    ## XML datatype
+  dbInt,                    ## some integer type
+  dbUInt,                   ## some unsigned integer type
+  dbDecimal,                ## decimal numbers (fixed-point number)
+  dbFloat,                  ## some floating point type
+  dbDate,                   ## a year-month-day description
+  dbTime,                   ## HH:MM:SS information
+  dbDatetime,               ## year-month-day and HH:MM:SS information,
+                             ## plus optional time or timezone information
+  dbTimestamp, ## Timestamp values are stored as the number of seconds
+                ## since the epoch ('1970-01-01 00:00:00' UTC).
+  dbTimeInterval,           ## an interval [a,b] of times
+  dbEnum,                   ## some enum
+  dbSet,                    ## set of enum values
+  dbArray,                  ## an array of values
+  dbComposite,              ## composite type (record, struct, etc)
+  dbUrl,                    ## a URL
+  dbUuid,                   ## a UUID
+  dbInet,                   ## an IP address
+  dbMacAddress,             ## a MAC address
+  dbGeometry,               ## some geometric type
+  dbPoint,                  ## Point on a plane   (x,y)
+  dbLine,                   ## Infinite line ((x1,y1),(x2,y2))
+  dbLseg,                   ## Finite line segment   ((x1,y1),(x2,y2))
+  dbBox,                    ## Rectangular box   ((x1,y1),(x2,y2))
+  dbPath,                   ## Closed or open path (similar to polygon) ((x1,y1),...)
+  dbPolygon,                ## Polygon (similar to closed path)   ((x1,y1),...)
+  dbCircle,                 ## Circle   <(x,y),r> (center point and radius)
+  dbUser1,                  ## user definable datatype 1 (for unknown extensions)
+  dbUser2,                  ## user definable datatype 2 (for unknown extensions)
+  dbUser3,                  ## user definable datatype 3 (for unknown extensions)
+  dbUser4,                  ## user definable datatype 4 (for unknown extensions)
+  dbUser5                    ## user definable datatype 5 (for unknown extensions)
+
+ + a superset of datatypes that might be supported. + +
+
+
+
ReadDbEffect = object of DbEffect
+
+ + effect that denotes a read operation + +
+
+
+
Row = seq[string]
+
+ + + +
+
+
+
WriteDbEffect = object of DbEffect
+
+ + effect that denotes a write operation + +
+
+ +
+
+
+

Consts

+
+
+
errorConnectionNum = 99999
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/database_types.idx b/clients/nim-allographer/docs/v2/query_builder/models/database_types.idx new file mode 100644 index 0000000..b2a3853 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/database_types.idx @@ -0,0 +1,51 @@ +nimTitle database_types query_builder/models/database_types.html module src/allographer/query_builder/models/database_types 0 +nim Row query_builder/models/database_types.html#Row type Row 2 +nim dbUnknown query_builder/models/database_types.html#dbUnknown DbTypeKind.dbUnknown 4 +nim dbSerial query_builder/models/database_types.html#dbSerial DbTypeKind.dbSerial 4 +nim dbNull query_builder/models/database_types.html#dbNull DbTypeKind.dbNull 4 +nim dbBit query_builder/models/database_types.html#dbBit DbTypeKind.dbBit 4 +nim dbBool query_builder/models/database_types.html#dbBool DbTypeKind.dbBool 4 +nim dbBlob query_builder/models/database_types.html#dbBlob DbTypeKind.dbBlob 4 +nim dbFixedChar query_builder/models/database_types.html#dbFixedChar DbTypeKind.dbFixedChar 4 +nim dbVarchar query_builder/models/database_types.html#dbVarchar DbTypeKind.dbVarchar 4 +nim dbJson query_builder/models/database_types.html#dbJson DbTypeKind.dbJson 4 +nim dbXml query_builder/models/database_types.html#dbXml DbTypeKind.dbXml 4 +nim dbInt query_builder/models/database_types.html#dbInt DbTypeKind.dbInt 4 +nim dbUInt query_builder/models/database_types.html#dbUInt DbTypeKind.dbUInt 4 +nim dbDecimal query_builder/models/database_types.html#dbDecimal DbTypeKind.dbDecimal 4 +nim dbFloat query_builder/models/database_types.html#dbFloat DbTypeKind.dbFloat 4 +nim dbDate query_builder/models/database_types.html#dbDate DbTypeKind.dbDate 4 +nim dbTime query_builder/models/database_types.html#dbTime DbTypeKind.dbTime 4 +nim dbDatetime query_builder/models/database_types.html#dbDatetime DbTypeKind.dbDatetime 4 +nim dbTimestamp query_builder/models/database_types.html#dbTimestamp DbTypeKind.dbTimestamp 4 +nim dbTimeInterval query_builder/models/database_types.html#dbTimeInterval DbTypeKind.dbTimeInterval 4 +nim dbEnum query_builder/models/database_types.html#dbEnum DbTypeKind.dbEnum 4 +nim dbSet query_builder/models/database_types.html#dbSet DbTypeKind.dbSet 4 +nim dbArray query_builder/models/database_types.html#dbArray DbTypeKind.dbArray 4 +nim dbComposite query_builder/models/database_types.html#dbComposite DbTypeKind.dbComposite 4 +nim dbUrl query_builder/models/database_types.html#dbUrl DbTypeKind.dbUrl 4 +nim dbUuid query_builder/models/database_types.html#dbUuid DbTypeKind.dbUuid 4 +nim dbInet query_builder/models/database_types.html#dbInet DbTypeKind.dbInet 4 +nim dbMacAddress query_builder/models/database_types.html#dbMacAddress DbTypeKind.dbMacAddress 4 +nim dbGeometry query_builder/models/database_types.html#dbGeometry DbTypeKind.dbGeometry 4 +nim dbPoint query_builder/models/database_types.html#dbPoint DbTypeKind.dbPoint 4 +nim dbLine query_builder/models/database_types.html#dbLine DbTypeKind.dbLine 4 +nim dbLseg query_builder/models/database_types.html#dbLseg DbTypeKind.dbLseg 4 +nim dbBox query_builder/models/database_types.html#dbBox DbTypeKind.dbBox 4 +nim dbPath query_builder/models/database_types.html#dbPath DbTypeKind.dbPath 4 +nim dbPolygon query_builder/models/database_types.html#dbPolygon DbTypeKind.dbPolygon 4 +nim dbCircle query_builder/models/database_types.html#dbCircle DbTypeKind.dbCircle 4 +nim dbUser1 query_builder/models/database_types.html#dbUser1 DbTypeKind.dbUser1 4 +nim dbUser2 query_builder/models/database_types.html#dbUser2 DbTypeKind.dbUser2 4 +nim dbUser3 query_builder/models/database_types.html#dbUser3 DbTypeKind.dbUser3 4 +nim dbUser4 query_builder/models/database_types.html#dbUser4 DbTypeKind.dbUser4 4 +nim dbUser5 query_builder/models/database_types.html#dbUser5 DbTypeKind.dbUser5 4 +nim DbTypeKind query_builder/models/database_types.html#DbTypeKind enum DbTypeKind 4 +nim DbType query_builder/models/database_types.html#DbType object DbType 48 +nim DbColumn query_builder/models/database_types.html#DbColumn object DbColumn 58 +nim DbColumns query_builder/models/database_types.html#DbColumns type DbColumns 65 +nim DbRows query_builder/models/database_types.html#DbRows type DbRows 66 +nim DbEffect query_builder/models/database_types.html#DbEffect object DbEffect 67 +nim ReadDbEffect query_builder/models/database_types.html#ReadDbEffect object ReadDbEffect 68 +nim WriteDbEffect query_builder/models/database_types.html#WriteDbEffect object WriteDbEffect 69 +nim errorConnectionNum query_builder/models/database_types.html#errorConnectionNum const errorConnectionNum 71 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.html b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.html new file mode 100644 index 0000000..794a362 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.html @@ -0,0 +1,674 @@ + + + + + + + +src/allographer/query_builder/models/mariadb/mariadb_exec + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mariadb/mariadb_exec

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avg(self: MariadbQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc begin(self: MariadbConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, KeyError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columns(self: MariadbQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + get columns sequence from table + +
+
+ +
+
+
+
proc commit(self: MariadbConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, KeyError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc count(self: MariadbQuery): Future[int] {....stackTrace: false, raises: [
+    Exception, ValueError, KeyError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc delete(self: MariadbQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc delete(self: MariadbQuery; id: int; key = "id"): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc exec(self: RawMariadbQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc find(self: MariadbQuery; id: int; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc find(self: MariadbQuery; id: string; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc findPlain(self: MariadbQuery; id: int; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc findPlain(self: MariadbQuery; id: string; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc first(self: MariadbQuery): Future[Option[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc first(self: RawMariadbQuery): Future[Option[JsonNode]] {....stackTrace: false, raises: [
+    Exception, ValueError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc firstPlain(self: MariadbQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc firstPlain(self: RawMariadbQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc get(self: MariadbQuery): Future[seq[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc get(self: RawMariadbQuery): Future[seq[JsonNode]] {....stackTrace: false, raises: [
+    Exception, ValueError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc getPlain(self: MariadbQuery): Future[seq[seq[string]]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc getPlain(self: RawMariadbQuery): Future[seq[seq[string]]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc insert(self: MariadbQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + items is JObject + +
+
+
+
proc insert(self: MariadbQuery; items: seq[JsonNode]): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insert[T](self: MariadbQuery; items: seq[T]): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+
+
proc insert[T](self: MariadbQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc insertId(self: MariadbQuery; items: JsonNode; key = "id"): Future[string] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insertId(self: MariadbQuery; items: seq[JsonNode]; key = "id"): Future[
+    seq[string]] {....stackTrace: false, raises: [Exception, ValueError, KeyError,
+    OSError, IOError, DbError], tags: [RootEffect, WriteDirEffect,
+                                       ReadDirEffect, ReadIOEffect,
+                                       WriteIOEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insertId[T](self: MariadbQuery; items: seq[T]; key = "id"): Future[
+    seq[string]] {....stackTrace: false.}
+
+ + + +
+
+
+
proc insertId[T](self: MariadbQuery; items: T; key = "id"): Future[string] {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc max(self: MariadbQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc min(self: MariadbQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rollback(self: MariadbConnections): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, KeyError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sum(self: MariadbQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc update(self: MariadbQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc update[T](self: MariadbQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+ +
+
+
+

Templates

+
+
+
+
template seeder(rdb: MariadbConnections; tableName, column: string;
+                body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table or specified column is empty. + +
+
+
+
template seeder(rdb: MariadbConnections; tableName: string; body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table is empty. + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.idx b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.idx new file mode 100644 index 0000000..25ec46d --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_exec.idx @@ -0,0 +1,48 @@ +nimTitle mariadb_exec query_builder/models/mariadb/mariadb_exec.html module src/allographer/query_builder/models/mariadb/mariadb_exec 0 +nim get query_builder/models/mariadb/mariadb_exec.html#get,MariadbQuery proc get(self: MariadbQuery): Future[seq[JsonNode]] 383 +nim first query_builder/models/mariadb/mariadb_exec.html#first,MariadbQuery proc first(self: MariadbQuery): Future[Option[JsonNode]] 394 +nim find query_builder/models/mariadb/mariadb_exec.html#find,MariadbQuery,string,string proc find(self: MariadbQuery; id: string; key = "id"): Future[Option[JsonNode]] 405 +nim find query_builder/models/mariadb/mariadb_exec.html#find,MariadbQuery,int,string proc find(self: MariadbQuery; id: int; key = "id"): Future[Option[JsonNode]] 417 +nim getPlain query_builder/models/mariadb/mariadb_exec.html#getPlain,MariadbQuery proc getPlain(self: MariadbQuery): Future[seq[seq[string]]] 421 +nim firstPlain query_builder/models/mariadb/mariadb_exec.html#firstPlain,MariadbQuery proc firstPlain(self: MariadbQuery): Future[seq[string]] 432 +nim findPlain query_builder/models/mariadb/mariadb_exec.html#findPlain,MariadbQuery,string,string proc findPlain(self: MariadbQuery; id: string; key = "id"): Future[seq[string]] 443 +nim findPlain query_builder/models/mariadb/mariadb_exec.html#findPlain,MariadbQuery,int,string proc findPlain(self: MariadbQuery; id: int; key = "id"): Future[seq[string]] 455 +nim insert query_builder/models/mariadb/mariadb_exec.html#insert,MariadbQuery,JsonNode proc insert(self: MariadbQuery; items: JsonNode): owned(Future[void]) 460 +nim insert query_builder/models/mariadb/mariadb_exec.html#insert,MariadbQuery,seq[JsonNode] proc insert(self: MariadbQuery; items: seq[JsonNode]): owned(Future[void]) 467 +nim insertId query_builder/models/mariadb/mariadb_exec.html#insertId,MariadbQuery,JsonNode,string proc insertId(self: MariadbQuery; items: JsonNode; key = "id"): Future[string] 473 +nim insertId query_builder/models/mariadb/mariadb_exec.html#insertId,MariadbQuery,seq[JsonNode],string proc insertId(self: MariadbQuery; items: seq[JsonNode]; key = "id"): Future[\n seq[string]] 480 +nim insert query_builder/models/mariadb/mariadb_exec.html#insert,MariadbQuery,T proc insert[T](self: MariadbQuery; items: T): owned(Future[void]) 491 +nim insert query_builder/models/mariadb/mariadb_exec.html#insert,MariadbQuery,seq[T] proc insert[T](self: MariadbQuery; items: seq[T]): owned(Future[void]) 497 +nim insertId query_builder/models/mariadb/mariadb_exec.html#insertId,MariadbQuery,T,string proc insertId[T](self: MariadbQuery; items: T; key = "id"): Future[string] 504 +nim insertId query_builder/models/mariadb/mariadb_exec.html#insertId,MariadbQuery,seq[T],string proc insertId[T](self: MariadbQuery; items: seq[T]; key = "id"): Future[seq[string]] 511 +nim update query_builder/models/mariadb/mariadb_exec.html#update,MariadbQuery,JsonNode proc update(self: MariadbQuery; items: JsonNode): owned(Future[void]) 522 +nim update query_builder/models/mariadb/mariadb_exec.html#update,MariadbQuery,T proc update[T](self: MariadbQuery; items: T): owned(Future[void]) 528 +nim delete query_builder/models/mariadb/mariadb_exec.html#delete,MariadbQuery proc delete(self: MariadbQuery): owned(Future[void]) 534 +nim delete query_builder/models/mariadb/mariadb_exec.html#delete,MariadbQuery,int,string proc delete(self: MariadbQuery; id: int; key = "id"): owned(Future[void]) 540 +nim columns query_builder/models/mariadb/mariadb_exec.html#columns,MariadbQuery proc columns(self: MariadbQuery): Future[seq[string]] 547 +nim count query_builder/models/mariadb/mariadb_exec.html#count,MariadbQuery proc count(self: MariadbQuery): Future[int] 559 +nim min query_builder/models/mariadb/mariadb_exec.html#min,MariadbQuery,string proc min(self: MariadbQuery; column: string): Future[Option[string]] 569 +nim max query_builder/models/mariadb/mariadb_exec.html#max,MariadbQuery,string proc max(self: MariadbQuery; column: string): Future[Option[string]] 585 +nim avg query_builder/models/mariadb/mariadb_exec.html#avg,MariadbQuery,string proc avg(self: MariadbQuery; column: string): Future[Option[float]] 601 +nim sum query_builder/models/mariadb/mariadb_exec.html#sum,MariadbQuery,string proc sum(self: MariadbQuery; column: string): Future[Option[float]] 611 +nim begin query_builder/models/mariadb/mariadb_exec.html#begin,MariadbConnections proc begin(self: MariadbConnections): owned(Future[void]) 621 +nim rollback query_builder/models/mariadb/mariadb_exec.html#rollback,MariadbConnections proc rollback(self: MariadbConnections): owned(Future[void]) 626 +nim commit query_builder/models/mariadb/mariadb_exec.html#commit,MariadbConnections proc commit(self: MariadbConnections): owned(Future[void]) 631 +nim get query_builder/models/mariadb/mariadb_exec.html#get,RawMariadbQuery proc get(self: RawMariadbQuery): Future[seq[JsonNode]] 636 +nim getPlain query_builder/models/mariadb/mariadb_exec.html#getPlain,RawMariadbQuery proc getPlain(self: RawMariadbQuery): Future[seq[seq[string]]] 642 +nim exec query_builder/models/mariadb/mariadb_exec.html#exec,RawMariadbQuery proc exec(self: RawMariadbQuery): owned(Future[void]) 648 +nim first query_builder/models/mariadb/mariadb_exec.html#first,RawMariadbQuery proc first(self: RawMariadbQuery): Future[Option[JsonNode]] 654 +nim firstPlain query_builder/models/mariadb/mariadb_exec.html#firstPlain,RawMariadbQuery proc firstPlain(self: RawMariadbQuery): Future[seq[string]] 660 +nim seeder query_builder/models/mariadb/mariadb_exec.html#seeder.t,MariadbConnections,string,untyped template seeder(rdb: MariadbConnections; tableName: string; body: untyped): untyped 666 +nim seeder query_builder/models/mariadb/mariadb_exec.html#seeder.t,MariadbConnections,string,string,untyped template seeder(rdb: MariadbConnections; tableName, column: string; body: untyped): untyped 673 +nimgrp getplain query_builder/models/mariadb/mariadb_exec.html#getPlain-procs-all proc 421 +nimgrp firstplain query_builder/models/mariadb/mariadb_exec.html#firstPlain-procs-all proc 432 +nimgrp findplain query_builder/models/mariadb/mariadb_exec.html#findPlain-procs-all proc 443 +nimgrp insertid query_builder/models/mariadb/mariadb_exec.html#insertId-procs-all proc 473 +nimgrp delete query_builder/models/mariadb/mariadb_exec.html#delete-procs-all proc 534 +nimgrp get query_builder/models/mariadb/mariadb_exec.html#get-procs-all proc 383 +nimgrp update query_builder/models/mariadb/mariadb_exec.html#update-procs-all proc 522 +nimgrp find query_builder/models/mariadb/mariadb_exec.html#find-procs-all proc 405 +nimgrp first query_builder/models/mariadb/mariadb_exec.html#first-procs-all proc 394 +nimgrp insert query_builder/models/mariadb/mariadb_exec.html#insert-procs-all proc 460 +nimgrp seeder query_builder/models/mariadb/mariadb_exec.html#seeder-templates-all template 666 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.html b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.html new file mode 100644 index 0000000..cb33843 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.html @@ -0,0 +1,147 @@ + + + + + + + +src/allographer/query_builder/models/mariadb/mariadb_open + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mariadb/mariadb_open

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dbOpen(_: type MariaDB; database: string; user: string; password: string;
+            host: string; port: int; maxConnections = 1; timeout = 30;
+            shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MariadbConnections
+
+ + + +
+
+
+
proc dbOpen(_: type MariaDB; databaseUrl: DatabaseUrl; maxConnections = 1;
+            timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;
+            logDir = ""): MariadbConnections
+
+ + + +
+
+
+
proc dbOpen(_: type MariaDB; url: string; maxConnections: int = 1; timeout = 30;
+            shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MariadbConnections
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.idx b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.idx new file mode 100644 index 0000000..e3ae7b7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_open.idx @@ -0,0 +1,5 @@ +nimTitle mariadb_open query_builder/models/mariadb/mariadb_open.html module src/allographer/query_builder/models/mariadb/mariadb_open 0 +nim dbOpen query_builder/models/mariadb/mariadb_open.html#dbOpen,typeMariaDB,string,string,string,string,int,int,int,string proc dbOpen(_: type MariaDB; database: string; user: string; password: string;\n host: string; port: int; maxConnections = 1; timeout = 30;\n shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MariadbConnections 9 +nim dbOpen query_builder/models/mariadb/mariadb_open.html#dbOpen,typeMariaDB,string,int,int,string proc dbOpen(_: type MariaDB; url: string; maxConnections: int = 1; timeout = 30;\n shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MariadbConnections 49 +nim dbOpen query_builder/models/mariadb/mariadb_open.html#dbOpen,typeMariaDB,DatabaseUrl,int,int,string proc dbOpen(_: type MariaDB; databaseUrl: DatabaseUrl; maxConnections = 1;\n timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;\n logDir = ""): MariadbConnections 54 +nimgrp dbopen query_builder/models/mariadb/mariadb_open.html#dbOpen-procs-all proc 9 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.html b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.html new file mode 100644 index 0000000..4149e82 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.html @@ -0,0 +1,471 @@ + + + + + + + +src/allographer/query_builder/models/mariadb/mariadb_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mariadb/mariadb_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc `distinct`(self: MariadbQuery): MariadbQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBy(self: MariadbQuery; column: string): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc having(self: MariadbQuery; column: string; symbol: string;
+            value: string | int | float | bool): MariadbQuery
+
+ + + +
+
+
+
proc having(self: MariadbQuery; column: string; symbol: string; value: nil.type): MariadbQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc join(self: MariadbQuery; table: string; column1: string; symbol: string;
+          column2: string): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoin(self: MariadbQuery; table: string; column1: string;
+              symbol: string; column2: string): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limit(self: MariadbQuery; num: int): MariadbQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offset(self: MariadbQuery; num: int): MariadbQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBy(self: MariadbQuery; column: string; order: Order): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhere(self: MariadbQuery; column: string; symbol: string;
+             value: string | int | float | bool): MariadbQuery
+
+ + + +
+
+
+
proc orWhere(self: MariadbQuery; column: string; symbol: string; value: nil.type): MariadbQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc raw(self: MariadbConnections; sql: string; arges = newJArray()): RawMariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ +

arges is JArray [true, 1, 1.1, "str"]

+

can't use BLOB data.

+ + +
+
+ +
+
+
+
proc select(self: MariadbConnections; columnsArg: varargs[string]): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc table(self: MariadbConnections; tableArg: string): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc table(self: MariadbQuery; tableArg: string): MariadbQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc where(self: MariadbQuery; column: string; symbol: string;
+           value: string | int | float): MariadbQuery
+
+ + + +
+
+
+
proc where(self: MariadbQuery; column: string; symbol: string; value: bool): MariadbQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc where(self: MariadbQuery; column: string; symbol: string; value: nil.type): MariadbQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetween(self: MariadbQuery; column: string;
+                  width: array[2, int | float]): MariadbQuery
+
+ + + +
+
+
+
proc whereBetween(self: MariadbQuery; column: string; width: array[2, string]): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereIn(self: MariadbQuery; column: string;
+             width: seq[int | float | string]): MariadbQuery
+
+ + + +
+
+ +
+
+
+
proc whereNotBetween(self: MariadbQuery; column: string;
+                     width: array[2, int | float]): MariadbQuery
+
+ + + +
+
+
+
proc whereNotBetween(self: MariadbQuery; column: string; width: array[2, string]): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotIn(self: MariadbQuery; column: string;
+                width: seq[int | float | string]): MariadbQuery
+
+ + + +
+
+ +
+
+
+
proc whereNull(self: MariadbQuery; column: string): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.idx b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.idx new file mode 100644 index 0000000..0e9f2d1 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_query.idx @@ -0,0 +1,32 @@ +nimTitle mariadb_query query_builder/models/mariadb/mariadb_query.html module src/allographer/query_builder/models/mariadb/mariadb_query 0 +nim select query_builder/models/mariadb/mariadb_query.html#select,MariadbConnections,varargs[string] proc select(self: MariadbConnections; columnsArg: varargs[string]): MariadbQuery 11 +nim table query_builder/models/mariadb/mariadb_query.html#table,MariadbConnections,string proc table(self: MariadbConnections; tableArg: string): MariadbQuery 32 +nim table query_builder/models/mariadb/mariadb_query.html#table,MariadbQuery,string proc table(self: MariadbQuery; tableArg: string): MariadbQuery 48 +nim `distinct` query_builder/models/mariadb/mariadb_query.html#distinct,MariadbQuery proc `distinct`(self: MariadbQuery): MariadbQuery 53 +nim join query_builder/models/mariadb/mariadb_query.html#join,MariadbQuery,string,string,string,string proc join(self: MariadbQuery; table: string; column1: string; symbol: string;\n column2: string): MariadbQuery 59 +nim leftJoin query_builder/models/mariadb/mariadb_query.html#leftJoin,MariadbQuery,string,string,string,string proc leftJoin(self: MariadbQuery; table: string; column1: string; symbol: string;\n column2: string): MariadbQuery 78 +nim where query_builder/models/mariadb/mariadb_query.html#where,MariadbQuery,string,string, proc where(self: MariadbQuery; column: string; symbol: string;\n value: string | int | float): MariadbQuery 100 +nim where query_builder/models/mariadb/mariadb_query.html#where,MariadbQuery,string,string,bool proc where(self: MariadbQuery; column: string; symbol: string; value: bool): MariadbQuery 127 +nim where query_builder/models/mariadb/mariadb_query.html#where,MariadbQuery,string,string,_2 proc where(self: MariadbQuery; column: string; symbol: string; value: nil.type): MariadbQuery 154 +nim orWhere query_builder/models/mariadb/mariadb_query.html#orWhere,MariadbQuery,string,string, proc orWhere(self: MariadbQuery; column: string; symbol: string;\n value: string | int | float | bool): MariadbQuery 178 +nim orWhere query_builder/models/mariadb/mariadb_query.html#orWhere,MariadbQuery,string,string,_2 proc orWhere(self: MariadbQuery; column: string; symbol: string; value: nil.type): MariadbQuery 205 +nim whereBetween query_builder/models/mariadb/mariadb_query.html#whereBetween,MariadbQuery,string,array[,] proc whereBetween(self: MariadbQuery; column: string; width: array[2, int | float]): MariadbQuery 227 +nim whereBetween query_builder/models/mariadb/mariadb_query.html#whereBetween,MariadbQuery,string,array[,string] proc whereBetween(self: MariadbQuery; column: string; width: array[2, string]): MariadbQuery 244 +nim whereNotBetween query_builder/models/mariadb/mariadb_query.html#whereNotBetween,MariadbQuery,string,array[,] proc whereNotBetween(self: MariadbQuery; column: string; width: array[2, int | float]): MariadbQuery 261 +nim whereNotBetween query_builder/models/mariadb/mariadb_query.html#whereNotBetween,MariadbQuery,string,array[,string] proc whereNotBetween(self: MariadbQuery; column: string; width: array[2, string]): MariadbQuery 278 +nim whereIn query_builder/models/mariadb/mariadb_query.html#whereIn,MariadbQuery,string,seq[] proc whereIn(self: MariadbQuery; column: string; width: seq[int | float | string]): MariadbQuery 295 +nim whereNotIn query_builder/models/mariadb/mariadb_query.html#whereNotIn,MariadbQuery,string,seq[] proc whereNotIn(self: MariadbQuery; column: string; width: seq[int | float | string]): MariadbQuery 312 +nim whereNull query_builder/models/mariadb/mariadb_query.html#whereNull,MariadbQuery,string proc whereNull(self: MariadbQuery; column: string): MariadbQuery 329 +nim groupBy query_builder/models/mariadb/mariadb_query.html#groupBy,MariadbQuery,string proc groupBy(self: MariadbQuery; column: string): MariadbQuery 343 +nim having query_builder/models/mariadb/mariadb_query.html#having,MariadbQuery,string,string, proc having(self: MariadbQuery; column: string; symbol: string;\n value: string | int | float | bool): MariadbQuery 351 +nim having query_builder/models/mariadb/mariadb_query.html#having,MariadbQuery,string,string,_2 proc having(self: MariadbQuery; column: string; symbol: string; value: nil.type): MariadbQuery 377 +nim orderBy query_builder/models/mariadb/mariadb_query.html#orderBy,MariadbQuery,string,Order proc orderBy(self: MariadbQuery; column: string; order: Order): MariadbQuery 401 +nim limit query_builder/models/mariadb/mariadb_query.html#limit,MariadbQuery,int proc limit(self: MariadbQuery; num: int): MariadbQuery 415 +nim offset query_builder/models/mariadb/mariadb_query.html#offset,MariadbQuery,int proc offset(self: MariadbQuery; num: int): MariadbQuery 420 +nim raw query_builder/models/mariadb/mariadb_query.html#raw,MariadbConnections,string proc raw(self: MariadbConnections; sql: string; arges = newJArray()): RawMariadbQuery 425 +nimgrp where query_builder/models/mariadb/mariadb_query.html#where-procs-all proc 100 +nimgrp wherebetween query_builder/models/mariadb/mariadb_query.html#whereBetween-procs-all proc 227 +nimgrp wherenotbetween query_builder/models/mariadb/mariadb_query.html#whereNotBetween-procs-all proc 261 +nimgrp orwhere query_builder/models/mariadb/mariadb_query.html#orWhere-procs-all proc 178 +nimgrp having query_builder/models/mariadb/mariadb_query.html#having-procs-all proc 351 +nimgrp table query_builder/models/mariadb/mariadb_query.html#table-procs-all proc 32 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.html b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.html new file mode 100644 index 0000000..c7d1e51 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.html @@ -0,0 +1,114 @@ + + + + + + + +src/allographer/query_builder/models/mariadb/mariadb_transaction + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mariadb/mariadb_transaction

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Templates

+
+
+
+
template transaction(rdb: MariadbConnections; callback: untyped)
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.idx b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.idx new file mode 100644 index 0000000..0d2d4ca --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_transaction.idx @@ -0,0 +1,2 @@ +nimTitle mariadb_transaction query_builder/models/mariadb/mariadb_transaction.html module src/allographer/query_builder/models/mariadb/mariadb_transaction 0 +nim transaction query_builder/models/mariadb/mariadb_transaction.html#transaction.t,MariadbConnections,untyped template transaction(rdb: MariadbConnections; callback: untyped) 41 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.html b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.html new file mode 100644 index 0000000..b6241b2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.html @@ -0,0 +1,272 @@ + + + + + + + +src/allographer/query_builder/models/mariadb/mariadb_types + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mariadb/mariadb_types

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

created by let rdb = dbOpen(MySQL, "localhost", 3306)created by rdb.select("columnName") or rdb.table("tableName")

+ +
+

Types

+
+
+
Connection = object
+  conn*: PMySQL
+  isBusy*: bool
+  createdAt*: int64
+
+ + + +
+
+
+
ConnectionInfo = object
+  database*: string
+  user*: string
+  password*: string
+  host*: string
+  port*: int
+
+ + + +
+
+
+
Connections = ref object
+  conns*: seq[Connection]
+  timeout*: int
+  waiters*: Deque[Future[void]]
+  columnTypeCache*: Table[string, seq[seq[string]]]
+
+ + + +
+
+
+
MariaDB = object
+
+ + + +
+
+
+
MariadbConnections = ref object
+  log*: LogSetting
+  pools*: Connections
+  info*: ConnectionInfo
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
MariadbQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  info*: ConnectionInfo
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
RawMariadbQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  info*: ConnectionInfo
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc `$`(self: MariadbConnections | MariadbQuery | RawMariadbQuery): string
+
+ + + +
+
+ +
+
+
+
proc isConnected(self: MariadbConnections | MariadbQuery | RawMariadbQuery): bool
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.idx b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.idx new file mode 100644 index 0000000..09bca58 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/mariadb_types.idx @@ -0,0 +1,10 @@ +nimTitle mariadb_types query_builder/models/mariadb/mariadb_types.html module src/allographer/query_builder/models/mariadb/mariadb_types 0 +nim MariaDB query_builder/models/mariadb/mariadb_types.html#MariaDB object MariaDB 9 +nim ConnectionInfo query_builder/models/mariadb/mariadb_types.html#ConnectionInfo object ConnectionInfo 12 +nim Connection query_builder/models/mariadb/mariadb_types.html#Connection object Connection 20 +nim Connections query_builder/models/mariadb/mariadb_types.html#Connections type Connections 26 +nim MariadbConnections query_builder/models/mariadb/mariadb_types.html#MariadbConnections type MariadbConnections 34 +nim MariadbQuery query_builder/models/mariadb/mariadb_types.html#MariadbQuery type MariadbQuery 44 +nim RawMariadbQuery query_builder/models/mariadb/mariadb_types.html#RawMariadbQuery type RawMariadbQuery 56 +nim `$` query_builder/models/mariadb/mariadb_types.html#$ proc `$`(self: MariadbConnections | MariadbQuery | RawMariadbQuery): string 68 +nim isConnected query_builder/models/mariadb/mariadb_types.html#isConnected proc isConnected(self: MariadbConnections | MariadbQuery | RawMariadbQuery): bool 72 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.html b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.html new file mode 100644 index 0000000..fa90aa6 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.html @@ -0,0 +1,323 @@ + + + + + + + +src/allographer/query_builder/models/mariadb/query/mariadb_builder + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mariadb/query/mariadb_builder

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avgBuilder(self: MariadbQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columnBuilder(self: MariadbQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc countBuilder(self: MariadbQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteBuilder(self: MariadbQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteByIdBuilder(self: MariadbQuery; id: int; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueBuilder(self: MariadbQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesBuilder(self: MariadbQuery; rows: openArray[JsonNode]): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc maxBuilder(self: MariadbQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc minBuilder(self: MariadbQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectBuilder(self: MariadbQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFindBuilder(self: MariadbQuery; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstBuilder(self: MariadbQuery): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sumBuilder(self: MariadbQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateBuilder(self: MariadbQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.idx b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.idx new file mode 100644 index 0000000..d8a14cc --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_builder.idx @@ -0,0 +1,15 @@ +nimTitle mariadb_builder query_builder/models/mariadb/query/mariadb_builder.html module src/allographer/query_builder/models/mariadb/query/mariadb_builder 0 +nim selectBuilder query_builder/models/mariadb/query/mariadb_builder.html#selectBuilder,MariadbQuery proc selectBuilder(self: MariadbQuery): string 8 +nim selectFirstBuilder query_builder/models/mariadb/query/mariadb_builder.html#selectFirstBuilder,MariadbQuery proc selectFirstBuilder(self: MariadbQuery): string 30 +nim selectFindBuilder query_builder/models/mariadb/query/mariadb_builder.html#selectFindBuilder,MariadbQuery,string proc selectFindBuilder(self: MariadbQuery; key: string): string 52 +nim insertValueBuilder query_builder/models/mariadb/query/mariadb_builder.html#insertValueBuilder,MariadbQuery,JsonNode proc insertValueBuilder(self: MariadbQuery; items: JsonNode): string 73 +nim insertValuesBuilder query_builder/models/mariadb/query/mariadb_builder.html#insertValuesBuilder,MariadbQuery,openArray[JsonNode] proc insertValuesBuilder(self: MariadbQuery; rows: openArray[JsonNode]): string 79 +nim updateBuilder query_builder/models/mariadb/query/mariadb_builder.html#updateBuilder,MariadbQuery,JsonNode proc updateBuilder(self: MariadbQuery; items: JsonNode): string 88 +nim deleteBuilder query_builder/models/mariadb/query/mariadb_builder.html#deleteBuilder,MariadbQuery proc deleteBuilder(self: MariadbQuery): string 101 +nim deleteByIdBuilder query_builder/models/mariadb/query/mariadb_builder.html#deleteByIdBuilder,MariadbQuery,int,string proc deleteByIdBuilder(self: MariadbQuery; id: int; key: string): string 113 +nim countBuilder query_builder/models/mariadb/query/mariadb_builder.html#countBuilder,MariadbQuery proc countBuilder(self: MariadbQuery): string 123 +nim maxBuilder query_builder/models/mariadb/query/mariadb_builder.html#maxBuilder,MariadbQuery,string proc maxBuilder(self: MariadbQuery; column: string): string 143 +nim minBuilder query_builder/models/mariadb/query/mariadb_builder.html#minBuilder,MariadbQuery,string proc minBuilder(self: MariadbQuery; column: string): string 163 +nim avgBuilder query_builder/models/mariadb/query/mariadb_builder.html#avgBuilder,MariadbQuery,string proc avgBuilder(self: MariadbQuery; column: string): string 183 +nim sumBuilder query_builder/models/mariadb/query/mariadb_builder.html#sumBuilder,MariadbQuery,string proc sumBuilder(self: MariadbQuery; column: string): string 203 +nim columnBuilder query_builder/models/mariadb/query/mariadb_builder.html#columnBuilder,MariadbQuery proc columnBuilder(self: MariadbQuery): string 223 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.html b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.html new file mode 100644 index 0000000..67e49d8 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.html @@ -0,0 +1,611 @@ + + + + + + + +src/allographer/query_builder/models/mariadb/query/mariadb_generator + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mariadb/query/mariadb_generator

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc deleteByIdSql(self: MariadbQuery; id: int; key: string): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteSql(self: MariadbQuery): MariadbQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBySql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc havingSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueSql(self: MariadbQuery; items: JsonNode): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesSql(self: MariadbQuery; rows: openArray[JsonNode]): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc joinSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoinSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limitSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offsetSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBySql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhereSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectAvgSql(self: MariadbQuery; column: string): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectByIdSql(self: MariadbQuery; key: string): MariadbQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectCountSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstSql(self: MariadbQuery): MariadbQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMaxSql(self: MariadbQuery; column: string): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMinSql(self: MariadbQuery; column: string): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSumSql(self: MariadbQuery; column: string): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateValuesSql(self: MariadbQuery; items: JsonNode): MariadbQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenStringSql(self: MariadbQuery): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereInSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenStringSql(self: MariadbQuery): MariadbQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotInSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNullSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereSql(self: MariadbQuery): MariadbQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.idx b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.idx new file mode 100644 index 0000000..0aeba16 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mariadb/query/mariadb_generator.idx @@ -0,0 +1,33 @@ +nimTitle mariadb_generator query_builder/models/mariadb/query/mariadb_generator.html module src/allographer/query_builder/models/mariadb/query/mariadb_generator 0 +nim selectSql query_builder/models/mariadb/query/mariadb_generator.html#selectSql,MariadbQuery proc selectSql(self: MariadbQuery): MariadbQuery 43 +nim fromSql query_builder/models/mariadb/query/mariadb_generator.html#fromSql,MariadbQuery proc fromSql(self: MariadbQuery): MariadbQuery 64 +nim selectFirstSql query_builder/models/mariadb/query/mariadb_generator.html#selectFirstSql,MariadbQuery proc selectFirstSql(self: MariadbQuery): MariadbQuery 70 +nim selectByIdSql query_builder/models/mariadb/query/mariadb_generator.html#selectByIdSql,MariadbQuery,string proc selectByIdSql(self: MariadbQuery; key: string): MariadbQuery 75 +nim joinSql query_builder/models/mariadb/query/mariadb_generator.html#joinSql,MariadbQuery proc joinSql(self: MariadbQuery): MariadbQuery 84 +nim leftJoinSql query_builder/models/mariadb/query/mariadb_generator.html#leftJoinSql,MariadbQuery proc leftJoinSql(self: MariadbQuery): MariadbQuery 96 +nim whereSql query_builder/models/mariadb/query/mariadb_generator.html#whereSql,MariadbQuery proc whereSql(self: MariadbQuery): MariadbQuery 108 +nim orWhereSql query_builder/models/mariadb/query/mariadb_generator.html#orWhereSql,MariadbQuery proc orWhereSql(self: MariadbQuery): MariadbQuery 121 +nim whereBetweenSql query_builder/models/mariadb/query/mariadb_generator.html#whereBetweenSql,MariadbQuery proc whereBetweenSql(self: MariadbQuery): MariadbQuery 135 +nim whereBetweenStringSql query_builder/models/mariadb/query/mariadb_generator.html#whereBetweenStringSql,MariadbQuery proc whereBetweenStringSql(self: MariadbQuery): MariadbQuery 151 +nim whereNotBetweenSql query_builder/models/mariadb/query/mariadb_generator.html#whereNotBetweenSql,MariadbQuery proc whereNotBetweenSql(self: MariadbQuery): MariadbQuery 167 +nim whereNotBetweenStringSql query_builder/models/mariadb/query/mariadb_generator.html#whereNotBetweenStringSql,MariadbQuery proc whereNotBetweenStringSql(self: MariadbQuery): MariadbQuery 183 +nim whereInSql query_builder/models/mariadb/query/mariadb_generator.html#whereInSql,MariadbQuery proc whereInSql(self: MariadbQuery): MariadbQuery 199 +nim whereNotInSql query_builder/models/mariadb/query/mariadb_generator.html#whereNotInSql,MariadbQuery proc whereNotInSql(self: MariadbQuery): MariadbQuery 215 +nim whereNullSql query_builder/models/mariadb/query/mariadb_generator.html#whereNullSql,MariadbQuery proc whereNullSql(self: MariadbQuery): MariadbQuery 231 +nim groupBySql query_builder/models/mariadb/query/mariadb_generator.html#groupBySql,MariadbQuery proc groupBySql(self: MariadbQuery): MariadbQuery 243 +nim havingSql query_builder/models/mariadb/query/mariadb_generator.html#havingSql,MariadbQuery proc havingSql(self: MariadbQuery): MariadbQuery 254 +nim orderBySql query_builder/models/mariadb/query/mariadb_generator.html#orderBySql,MariadbQuery proc orderBySql(self: MariadbQuery): MariadbQuery 269 +nim limitSql query_builder/models/mariadb/query/mariadb_generator.html#limitSql,MariadbQuery proc limitSql(self: MariadbQuery): MariadbQuery 282 +nim offsetSql query_builder/models/mariadb/query/mariadb_generator.html#offsetSql,MariadbQuery proc offsetSql(self: MariadbQuery): MariadbQuery 290 +nim insertSql query_builder/models/mariadb/query/mariadb_generator.html#insertSql,MariadbQuery proc insertSql(self: MariadbQuery): MariadbQuery 300 +nim insertValueSql query_builder/models/mariadb/query/mariadb_generator.html#insertValueSql,MariadbQuery,JsonNode proc insertValueSql(self: MariadbQuery; items: JsonNode): MariadbQuery 306 +nim insertValuesSql query_builder/models/mariadb/query/mariadb_generator.html#insertValuesSql,MariadbQuery,openArray[JsonNode] proc insertValuesSql(self: MariadbQuery; rows: openArray[JsonNode]): MariadbQuery 326 +nim updateSql query_builder/models/mariadb/query/mariadb_generator.html#updateSql,MariadbQuery proc updateSql(self: MariadbQuery): MariadbQuery 358 +nim updateValuesSql query_builder/models/mariadb/query/mariadb_generator.html#updateValuesSql,MariadbQuery,JsonNode proc updateValuesSql(self: MariadbQuery; items: JsonNode): MariadbQuery 368 +nim deleteSql query_builder/models/mariadb/query/mariadb_generator.html#deleteSql,MariadbQuery proc deleteSql(self: MariadbQuery): MariadbQuery 390 +nim deleteByIdSql query_builder/models/mariadb/query/mariadb_generator.html#deleteByIdSql,MariadbQuery,int,string proc deleteByIdSql(self: MariadbQuery; id: int; key: string): MariadbQuery 395 +nim selectCountSql query_builder/models/mariadb/query/mariadb_generator.html#selectCountSql,MariadbQuery proc selectCountSql(self: MariadbQuery): MariadbQuery 401 +nim selectMaxSql query_builder/models/mariadb/query/mariadb_generator.html#selectMaxSql,MariadbQuery,string proc selectMaxSql(self: MariadbQuery; column: string): MariadbQuery 412 +nim selectMinSql query_builder/models/mariadb/query/mariadb_generator.html#selectMinSql,MariadbQuery,string proc selectMinSql(self: MariadbQuery; column: string): MariadbQuery 417 +nim selectAvgSql query_builder/models/mariadb/query/mariadb_generator.html#selectAvgSql,MariadbQuery,string proc selectAvgSql(self: MariadbQuery; column: string): MariadbQuery 422 +nim selectSumSql query_builder/models/mariadb/query/mariadb_generator.html#selectSumSql,MariadbQuery,string proc selectSumSql(self: MariadbQuery; column: string): MariadbQuery 427 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.html b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.html new file mode 100644 index 0000000..95ba13e --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.html @@ -0,0 +1,670 @@ + + + + + + + +src/allographer/query_builder/models/mysql/mysql_exec + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mysql/mysql_exec

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avg(self: MysqlQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc begin(self: MysqlConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, KeyError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columns(self: MysqlQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + get columns sequence from table + +
+
+ +
+
+
+
proc commit(self: MysqlConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, KeyError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc count(self: MysqlQuery): Future[int] {....stackTrace: false, raises: [
+    Exception, ValueError, KeyError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc delete(self: MysqlQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc delete(self: MysqlQuery; id: int; key = "id"): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc exec(self: RawMysqlQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc find(self: MysqlQuery; id: int; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc find(self: MysqlQuery; id: string; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc findPlain(self: MysqlQuery; id: int; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc findPlain(self: MysqlQuery; id: string; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc first(self: MysqlQuery): Future[Option[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc first(self: RawMysqlQuery): Future[Option[JsonNode]] {....stackTrace: false, raises: [
+    Exception, ValueError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc firstPlain(self: MysqlQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc firstPlain(self: RawMysqlQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc get(self: MysqlQuery): Future[seq[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc get(self: RawMysqlQuery): Future[seq[JsonNode]] {....stackTrace: false, raises: [
+    Exception, ValueError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc getPlain(self: MysqlQuery): Future[seq[seq[string]]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc getPlain(self: RawMysqlQuery): Future[seq[seq[string]]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc insert(self: MysqlQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + items is JObject + +
+
+
+
proc insert(self: MysqlQuery; items: seq[JsonNode]): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insert[T](self: MysqlQuery; items: seq[T]): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+
+
proc insert[T](self: MysqlQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc insertId(self: MysqlQuery; items: JsonNode; key = "id"): Future[string] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insertId(self: MysqlQuery; items: seq[JsonNode]; key = "id"): Future[
+    seq[string]] {....stackTrace: false, raises: [Exception, ValueError, KeyError,
+    OSError, IOError, DbError], tags: [RootEffect, WriteDirEffect,
+                                       ReadDirEffect, ReadIOEffect,
+                                       WriteIOEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insertId[T](self: MysqlQuery; items: seq[T]; key = "id"): Future[
+    seq[string]] {....stackTrace: false.}
+
+ + + +
+
+
+
proc insertId[T](self: MysqlQuery; items: T; key = "id"): Future[string] {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc max(self: MysqlQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc min(self: MysqlQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rollback(self: MysqlConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, KeyError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sum(self: MysqlQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc update(self: MysqlQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc update[T](self: MysqlQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+ +
+
+
+

Templates

+
+
+
+
template seeder(rdb: MysqlConnections; tableName, column: string; body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table or specified column is empty. + +
+
+
+
template seeder(rdb: MysqlConnections; tableName: string; body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table is empty. + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.idx b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.idx new file mode 100644 index 0000000..3c7e908 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_exec.idx @@ -0,0 +1,48 @@ +nimTitle mysql_exec query_builder/models/mysql/mysql_exec.html module src/allographer/query_builder/models/mysql/mysql_exec 0 +nim get query_builder/models/mysql/mysql_exec.html#get,MysqlQuery proc get(self: MysqlQuery): Future[seq[JsonNode]] 357 +nim first query_builder/models/mysql/mysql_exec.html#first,MysqlQuery proc first(self: MysqlQuery): Future[Option[JsonNode]] 368 +nim find query_builder/models/mysql/mysql_exec.html#find,MysqlQuery,string,string proc find(self: MysqlQuery; id: string; key = "id"): Future[Option[JsonNode]] 379 +nim find query_builder/models/mysql/mysql_exec.html#find,MysqlQuery,int,string proc find(self: MysqlQuery; id: int; key = "id"): Future[Option[JsonNode]] 391 +nim getPlain query_builder/models/mysql/mysql_exec.html#getPlain,MysqlQuery proc getPlain(self: MysqlQuery): Future[seq[seq[string]]] 395 +nim firstPlain query_builder/models/mysql/mysql_exec.html#firstPlain,MysqlQuery proc firstPlain(self: MysqlQuery): Future[seq[string]] 406 +nim findPlain query_builder/models/mysql/mysql_exec.html#findPlain,MysqlQuery,string,string proc findPlain(self: MysqlQuery; id: string; key = "id"): Future[seq[string]] 417 +nim findPlain query_builder/models/mysql/mysql_exec.html#findPlain,MysqlQuery,int,string proc findPlain(self: MysqlQuery; id: int; key = "id"): Future[seq[string]] 429 +nim insert query_builder/models/mysql/mysql_exec.html#insert,MysqlQuery,JsonNode proc insert(self: MysqlQuery; items: JsonNode): owned(Future[void]) 434 +nim insert query_builder/models/mysql/mysql_exec.html#insert,MysqlQuery,seq[JsonNode] proc insert(self: MysqlQuery; items: seq[JsonNode]): owned(Future[void]) 441 +nim insertId query_builder/models/mysql/mysql_exec.html#insertId,MysqlQuery,JsonNode,string proc insertId(self: MysqlQuery; items: JsonNode; key = "id"): Future[string] 447 +nim insertId query_builder/models/mysql/mysql_exec.html#insertId,MysqlQuery,seq[JsonNode],string proc insertId(self: MysqlQuery; items: seq[JsonNode]; key = "id"): Future[seq[string]] 454 +nim insert query_builder/models/mysql/mysql_exec.html#insert,MysqlQuery,T proc insert[T](self: MysqlQuery; items: T): owned(Future[void]) 465 +nim insert query_builder/models/mysql/mysql_exec.html#insert,MysqlQuery,seq[T] proc insert[T](self: MysqlQuery; items: seq[T]): owned(Future[void]) 471 +nim insertId query_builder/models/mysql/mysql_exec.html#insertId,MysqlQuery,T,string proc insertId[T](self: MysqlQuery; items: T; key = "id"): Future[string] 478 +nim insertId query_builder/models/mysql/mysql_exec.html#insertId,MysqlQuery,seq[T],string proc insertId[T](self: MysqlQuery; items: seq[T]; key = "id"): Future[seq[string]] 485 +nim update query_builder/models/mysql/mysql_exec.html#update,MysqlQuery,JsonNode proc update(self: MysqlQuery; items: JsonNode): owned(Future[void]) 495 +nim update query_builder/models/mysql/mysql_exec.html#update,MysqlQuery,T proc update[T](self: MysqlQuery; items: T): owned(Future[void]) 501 +nim delete query_builder/models/mysql/mysql_exec.html#delete,MysqlQuery proc delete(self: MysqlQuery): owned(Future[void]) 507 +nim delete query_builder/models/mysql/mysql_exec.html#delete,MysqlQuery,int,string proc delete(self: MysqlQuery; id: int; key = "id"): owned(Future[void]) 513 +nim columns query_builder/models/mysql/mysql_exec.html#columns,MysqlQuery proc columns(self: MysqlQuery): Future[seq[string]] 520 +nim count query_builder/models/mysql/mysql_exec.html#count,MysqlQuery proc count(self: MysqlQuery): Future[int] 532 +nim min query_builder/models/mysql/mysql_exec.html#min,MysqlQuery,string proc min(self: MysqlQuery; column: string): Future[Option[string]] 542 +nim max query_builder/models/mysql/mysql_exec.html#max,MysqlQuery,string proc max(self: MysqlQuery; column: string): Future[Option[string]] 558 +nim avg query_builder/models/mysql/mysql_exec.html#avg,MysqlQuery,string proc avg(self: MysqlQuery; column: string): Future[Option[float]] 574 +nim sum query_builder/models/mysql/mysql_exec.html#sum,MysqlQuery,string proc sum(self: MysqlQuery; column: string): Future[Option[float]] 584 +nim begin query_builder/models/mysql/mysql_exec.html#begin,MysqlConnections proc begin(self: MysqlConnections): owned(Future[void]) 594 +nim rollback query_builder/models/mysql/mysql_exec.html#rollback,MysqlConnections proc rollback(self: MysqlConnections): owned(Future[void]) 599 +nim commit query_builder/models/mysql/mysql_exec.html#commit,MysqlConnections proc commit(self: MysqlConnections): owned(Future[void]) 604 +nim get query_builder/models/mysql/mysql_exec.html#get,RawMysqlQuery proc get(self: RawMysqlQuery): Future[seq[JsonNode]] 609 +nim getPlain query_builder/models/mysql/mysql_exec.html#getPlain,RawMysqlQuery proc getPlain(self: RawMysqlQuery): Future[seq[seq[string]]] 615 +nim exec query_builder/models/mysql/mysql_exec.html#exec,RawMysqlQuery proc exec(self: RawMysqlQuery): owned(Future[void]) 621 +nim first query_builder/models/mysql/mysql_exec.html#first,RawMysqlQuery proc first(self: RawMysqlQuery): Future[Option[JsonNode]] 627 +nim firstPlain query_builder/models/mysql/mysql_exec.html#firstPlain,RawMysqlQuery proc firstPlain(self: RawMysqlQuery): Future[seq[string]] 633 +nim seeder query_builder/models/mysql/mysql_exec.html#seeder.t,MysqlConnections,string,untyped template seeder(rdb: MysqlConnections; tableName: string; body: untyped): untyped 639 +nim seeder query_builder/models/mysql/mysql_exec.html#seeder.t,MysqlConnections,string,string,untyped template seeder(rdb: MysqlConnections; tableName, column: string; body: untyped): untyped 646 +nimgrp getplain query_builder/models/mysql/mysql_exec.html#getPlain-procs-all proc 395 +nimgrp firstplain query_builder/models/mysql/mysql_exec.html#firstPlain-procs-all proc 406 +nimgrp findplain query_builder/models/mysql/mysql_exec.html#findPlain-procs-all proc 417 +nimgrp insertid query_builder/models/mysql/mysql_exec.html#insertId-procs-all proc 447 +nimgrp delete query_builder/models/mysql/mysql_exec.html#delete-procs-all proc 507 +nimgrp get query_builder/models/mysql/mysql_exec.html#get-procs-all proc 357 +nimgrp update query_builder/models/mysql/mysql_exec.html#update-procs-all proc 495 +nimgrp find query_builder/models/mysql/mysql_exec.html#find-procs-all proc 379 +nimgrp first query_builder/models/mysql/mysql_exec.html#first-procs-all proc 368 +nimgrp insert query_builder/models/mysql/mysql_exec.html#insert-procs-all proc 434 +nimgrp seeder query_builder/models/mysql/mysql_exec.html#seeder-templates-all template 639 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.html b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.html new file mode 100644 index 0000000..2468ee8 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.html @@ -0,0 +1,147 @@ + + + + + + + +src/allographer/query_builder/models/mysql/mysql_open + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mysql/mysql_open

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dbOpen(_: type MySQL; database: string; user: string; password: string;
+            host: string; port: int; maxConnections: int = 1; timeout = 30;
+            shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MysqlConnections
+
+ + + +
+
+
+
proc dbOpen(_: type MySQL; databaseUrl: DatabaseUrl; maxConnections = 1;
+            timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;
+            logDir = ""): MysqlConnections
+
+ + + +
+
+
+
proc dbOpen(_: type MySQL; url: string; maxConnections = 1; timeout = 30;
+            shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MysqlConnections
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.idx b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.idx new file mode 100644 index 0000000..331d9e3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_open.idx @@ -0,0 +1,5 @@ +nimTitle mysql_open query_builder/models/mysql/mysql_open.html module src/allographer/query_builder/models/mysql/mysql_open 0 +nim dbOpen query_builder/models/mysql/mysql_open.html#dbOpen,typeMySQL,string,string,string,string,int,int,int,string proc dbOpen(_: type MySQL; database: string; user: string; password: string;\n host: string; port: int; maxConnections: int = 1; timeout = 30;\n shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MysqlConnections 9 +nim dbOpen query_builder/models/mysql/mysql_open.html#dbOpen,typeMySQL,string,int,int,string proc dbOpen(_: type MySQL; url: string; maxConnections = 1; timeout = 30;\n shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): MysqlConnections 52 +nim dbOpen query_builder/models/mysql/mysql_open.html#dbOpen,typeMySQL,DatabaseUrl,int,int,string proc dbOpen(_: type MySQL; databaseUrl: DatabaseUrl; maxConnections = 1;\n timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;\n logDir = ""): MysqlConnections 57 +nimgrp dbopen query_builder/models/mysql/mysql_open.html#dbOpen-procs-all proc 9 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.html b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.html new file mode 100644 index 0000000..6f35f81 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.html @@ -0,0 +1,469 @@ + + + + + + + +src/allographer/query_builder/models/mysql/mysql_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mysql/mysql_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc `distinct`(self: MysqlQuery): MysqlQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBy(self: MysqlQuery; column: string): MysqlQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc having(self: MysqlQuery; column: string; symbol: string;
+            value: string | int | float | bool): MysqlQuery
+
+ + + +
+
+
+
proc having(self: MysqlQuery; column: string; symbol: string; value: nil.type): MysqlQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc join(self: MysqlQuery; table: string; column1: string; symbol: string;
+          column2: string): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoin(self: MysqlQuery; table: string; column1: string; symbol: string;
+              column2: string): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limit(self: MysqlQuery; num: int): MysqlQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offset(self: MysqlQuery; num: int): MysqlQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBy(self: MysqlQuery; column: string; order: Order): MysqlQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhere(self: MysqlQuery; column: string; symbol: string;
+             value: string | int | float | bool): MysqlQuery
+
+ + + +
+
+
+
proc orWhere(self: MysqlQuery; column: string; symbol: string; value: nil.type): MysqlQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc raw(self: MysqlConnections; sql: string; arges = newJArray()): RawMysqlQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ +

arges is JArray [true, 1, 1.1, "str"]

+

can't use BLOB data.

+ + +
+
+ +
+
+
+
proc select(self: MysqlConnections; columnsArg: varargs[string]): MysqlQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc table(self: MysqlConnections; tableArg: string): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+
+
proc table(self: MysqlQuery; tableArg: string): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc where(self: MysqlQuery; column: string; symbol: string;
+           value: string | int | float): MysqlQuery
+
+ + + +
+
+
+
proc where(self: MysqlQuery; column: string; symbol: string; value: bool): MysqlQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc where(self: MysqlQuery; column: string; symbol: string; value: nil.type): MysqlQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetween(self: MysqlQuery; column: string; width: array[2, int | float]): MysqlQuery
+
+ + + +
+
+
+
proc whereBetween(self: MysqlQuery; column: string; width: array[2, string]): MysqlQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereIn(self: MysqlQuery; column: string; width: seq[int | float | string]): MysqlQuery
+
+ + + +
+
+ +
+
+
+
proc whereNotBetween(self: MysqlQuery; column: string;
+                     width: array[2, int | float]): MysqlQuery
+
+ + + +
+
+
+
proc whereNotBetween(self: MysqlQuery; column: string; width: array[2, string]): MysqlQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotIn(self: MysqlQuery; column: string;
+                width: seq[int | float | string]): MysqlQuery
+
+ + + +
+
+ +
+
+
+
proc whereNull(self: MysqlQuery; column: string): MysqlQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.idx b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.idx new file mode 100644 index 0000000..85406a5 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_query.idx @@ -0,0 +1,32 @@ +nimTitle mysql_query query_builder/models/mysql/mysql_query.html module src/allographer/query_builder/models/mysql/mysql_query 0 +nim select query_builder/models/mysql/mysql_query.html#select,MysqlConnections,varargs[string] proc select(self: MysqlConnections; columnsArg: varargs[string]): MysqlQuery 11 +nim table query_builder/models/mysql/mysql_query.html#table,MysqlConnections,string proc table(self: MysqlConnections; tableArg: string): MysqlQuery 31 +nim table query_builder/models/mysql/mysql_query.html#table,MysqlQuery,string proc table(self: MysqlQuery; tableArg: string): MysqlQuery 47 +nim `distinct` query_builder/models/mysql/mysql_query.html#distinct,MysqlQuery proc `distinct`(self: MysqlQuery): MysqlQuery 52 +nim join query_builder/models/mysql/mysql_query.html#join,MysqlQuery,string,string,string,string proc join(self: MysqlQuery; table: string; column1: string; symbol: string;\n column2: string): MysqlQuery 58 +nim leftJoin query_builder/models/mysql/mysql_query.html#leftJoin,MysqlQuery,string,string,string,string proc leftJoin(self: MysqlQuery; table: string; column1: string; symbol: string;\n column2: string): MysqlQuery 77 +nim where query_builder/models/mysql/mysql_query.html#where,MysqlQuery,string,string, proc where(self: MysqlQuery; column: string; symbol: string;\n value: string | int | float): MysqlQuery 99 +nim where query_builder/models/mysql/mysql_query.html#where,MysqlQuery,string,string,bool proc where(self: MysqlQuery; column: string; symbol: string; value: bool): MysqlQuery 126 +nim where query_builder/models/mysql/mysql_query.html#where,MysqlQuery,string,string,_2 proc where(self: MysqlQuery; column: string; symbol: string; value: nil.type): MysqlQuery 153 +nim orWhere query_builder/models/mysql/mysql_query.html#orWhere,MysqlQuery,string,string, proc orWhere(self: MysqlQuery; column: string; symbol: string;\n value: string | int | float | bool): MysqlQuery 177 +nim orWhere query_builder/models/mysql/mysql_query.html#orWhere,MysqlQuery,string,string,_2 proc orWhere(self: MysqlQuery; column: string; symbol: string; value: nil.type): MysqlQuery 204 +nim whereBetween query_builder/models/mysql/mysql_query.html#whereBetween,MysqlQuery,string,array[,] proc whereBetween(self: MysqlQuery; column: string; width: array[2, int | float]): MysqlQuery 226 +nim whereBetween query_builder/models/mysql/mysql_query.html#whereBetween,MysqlQuery,string,array[,string] proc whereBetween(self: MysqlQuery; column: string; width: array[2, string]): MysqlQuery 243 +nim whereNotBetween query_builder/models/mysql/mysql_query.html#whereNotBetween,MysqlQuery,string,array[,] proc whereNotBetween(self: MysqlQuery; column: string; width: array[2, int | float]): MysqlQuery 260 +nim whereNotBetween query_builder/models/mysql/mysql_query.html#whereNotBetween,MysqlQuery,string,array[,string] proc whereNotBetween(self: MysqlQuery; column: string; width: array[2, string]): MysqlQuery 277 +nim whereIn query_builder/models/mysql/mysql_query.html#whereIn,MysqlQuery,string,seq[] proc whereIn(self: MysqlQuery; column: string; width: seq[int | float | string]): MysqlQuery 294 +nim whereNotIn query_builder/models/mysql/mysql_query.html#whereNotIn,MysqlQuery,string,seq[] proc whereNotIn(self: MysqlQuery; column: string; width: seq[int | float | string]): MysqlQuery 311 +nim whereNull query_builder/models/mysql/mysql_query.html#whereNull,MysqlQuery,string proc whereNull(self: MysqlQuery; column: string): MysqlQuery 328 +nim groupBy query_builder/models/mysql/mysql_query.html#groupBy,MysqlQuery,string proc groupBy(self: MysqlQuery; column: string): MysqlQuery 342 +nim having query_builder/models/mysql/mysql_query.html#having,MysqlQuery,string,string, proc having(self: MysqlQuery; column: string; symbol: string;\n value: string | int | float | bool): MysqlQuery 350 +nim having query_builder/models/mysql/mysql_query.html#having,MysqlQuery,string,string,_2 proc having(self: MysqlQuery; column: string; symbol: string; value: nil.type): MysqlQuery 376 +nim orderBy query_builder/models/mysql/mysql_query.html#orderBy,MysqlQuery,string,Order proc orderBy(self: MysqlQuery; column: string; order: Order): MysqlQuery 400 +nim limit query_builder/models/mysql/mysql_query.html#limit,MysqlQuery,int proc limit(self: MysqlQuery; num: int): MysqlQuery 414 +nim offset query_builder/models/mysql/mysql_query.html#offset,MysqlQuery,int proc offset(self: MysqlQuery; num: int): MysqlQuery 419 +nim raw query_builder/models/mysql/mysql_query.html#raw,MysqlConnections,string proc raw(self: MysqlConnections; sql: string; arges = newJArray()): RawMysqlQuery 424 +nimgrp where query_builder/models/mysql/mysql_query.html#where-procs-all proc 99 +nimgrp wherebetween query_builder/models/mysql/mysql_query.html#whereBetween-procs-all proc 226 +nimgrp wherenotbetween query_builder/models/mysql/mysql_query.html#whereNotBetween-procs-all proc 260 +nimgrp orwhere query_builder/models/mysql/mysql_query.html#orWhere-procs-all proc 177 +nimgrp having query_builder/models/mysql/mysql_query.html#having-procs-all proc 350 +nimgrp table query_builder/models/mysql/mysql_query.html#table-procs-all proc 31 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.html b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.html new file mode 100644 index 0000000..db1ace6 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.html @@ -0,0 +1,114 @@ + + + + + + + +src/allographer/query_builder/models/mysql/mysql_transaction + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mysql/mysql_transaction

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Imports

+
+ mysql_types +
+
+
+

Templates

+
+
+
+
template transaction(rdb: MysqlConnections; callback: untyped)
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.idx b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.idx new file mode 100644 index 0000000..bc08849 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_transaction.idx @@ -0,0 +1,2 @@ +nimTitle mysql_transaction query_builder/models/mysql/mysql_transaction.html module src/allographer/query_builder/models/mysql/mysql_transaction 0 +nim transaction query_builder/models/mysql/mysql_transaction.html#transaction.t,MysqlConnections,untyped template transaction(rdb: MysqlConnections; callback: untyped) 41 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.html b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.html new file mode 100644 index 0000000..dd1fd0b --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.html @@ -0,0 +1,268 @@ + + + + + + + +src/allographer/query_builder/models/mysql/mysql_types + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mysql/mysql_types

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

created by let rdb = dbOpen(MySQL, "localhost", 3306)created by rdb.select("columnName") or rdb.table("tableName")

+ +
+

Types

+
+
+
Connection = object
+  conn*: PMySQL
+  isBusy*: bool
+  createdAt*: int64
+
+ + + +
+
+
+
ConnectionInfo = object
+  database*: string
+  user*: string
+  password*: string
+  host*: string
+  port*: int
+
+ + + +
+
+
+
Connections = ref object
+  conns*: seq[Connection]
+  timeout*: int
+
+ + + +
+
+
+
MySQL = object
+
+ + + +
+
+
+
MysqlConnections = ref object
+  log*: LogSetting
+  pools*: Connections
+  info*: ConnectionInfo
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
MysqlQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  info*: ConnectionInfo
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
RawMysqlQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  info*: ConnectionInfo
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc `$`(self: MysqlConnections | MysqlQuery | RawMysqlQuery): string
+
+ + + +
+
+ +
+
+
+
proc isConnected(self: MysqlConnections | MysqlQuery | RawMysqlQuery): bool
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.idx b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.idx new file mode 100644 index 0000000..14ac28d --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/mysql_types.idx @@ -0,0 +1,10 @@ +nimTitle mysql_types query_builder/models/mysql/mysql_types.html module src/allographer/query_builder/models/mysql/mysql_types 0 +nim MySQL query_builder/models/mysql/mysql_types.html#MySQL object MySQL 6 +nim ConnectionInfo query_builder/models/mysql/mysql_types.html#ConnectionInfo object ConnectionInfo 9 +nim Connection query_builder/models/mysql/mysql_types.html#Connection object Connection 17 +nim Connections query_builder/models/mysql/mysql_types.html#Connections type Connections 23 +nim MysqlConnections query_builder/models/mysql/mysql_types.html#MysqlConnections type MysqlConnections 29 +nim MysqlQuery query_builder/models/mysql/mysql_types.html#MysqlQuery type MysqlQuery 39 +nim RawMysqlQuery query_builder/models/mysql/mysql_types.html#RawMysqlQuery type RawMysqlQuery 51 +nim `$` query_builder/models/mysql/mysql_types.html#$ proc `$`(self: MysqlConnections | MysqlQuery | RawMysqlQuery): string 63 +nim isConnected query_builder/models/mysql/mysql_types.html#isConnected proc isConnected(self: MysqlConnections | MysqlQuery | RawMysqlQuery): bool 67 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.html b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.html new file mode 100644 index 0000000..a135e48 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.html @@ -0,0 +1,323 @@ + + + + + + + +src/allographer/query_builder/models/mysql/query/mysql_builder + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mysql/query/mysql_builder

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avgBuilder(self: MysqlQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columnBuilder(self: MysqlQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc countBuilder(self: MysqlQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteBuilder(self: MysqlQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteByIdBuilder(self: MysqlQuery; id: int; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueBuilder(self: MysqlQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesBuilder(self: MysqlQuery; rows: openArray[JsonNode]): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc maxBuilder(self: MysqlQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc minBuilder(self: MysqlQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectBuilder(self: MysqlQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFindBuilder(self: MysqlQuery; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstBuilder(self: MysqlQuery): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sumBuilder(self: MysqlQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateBuilder(self: MysqlQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.idx b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.idx new file mode 100644 index 0000000..1acb4f7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_builder.idx @@ -0,0 +1,15 @@ +nimTitle mysql_builder query_builder/models/mysql/query/mysql_builder.html module src/allographer/query_builder/models/mysql/query/mysql_builder 0 +nim selectBuilder query_builder/models/mysql/query/mysql_builder.html#selectBuilder,MysqlQuery proc selectBuilder(self: MysqlQuery): string 8 +nim selectFirstBuilder query_builder/models/mysql/query/mysql_builder.html#selectFirstBuilder,MysqlQuery proc selectFirstBuilder(self: MysqlQuery): string 30 +nim selectFindBuilder query_builder/models/mysql/query/mysql_builder.html#selectFindBuilder,MysqlQuery,string proc selectFindBuilder(self: MysqlQuery; key: string): string 52 +nim insertValueBuilder query_builder/models/mysql/query/mysql_builder.html#insertValueBuilder,MysqlQuery,JsonNode proc insertValueBuilder(self: MysqlQuery; items: JsonNode): string 73 +nim insertValuesBuilder query_builder/models/mysql/query/mysql_builder.html#insertValuesBuilder,MysqlQuery,openArray[JsonNode] proc insertValuesBuilder(self: MysqlQuery; rows: openArray[JsonNode]): string 79 +nim updateBuilder query_builder/models/mysql/query/mysql_builder.html#updateBuilder,MysqlQuery,JsonNode proc updateBuilder(self: MysqlQuery; items: JsonNode): string 88 +nim deleteBuilder query_builder/models/mysql/query/mysql_builder.html#deleteBuilder,MysqlQuery proc deleteBuilder(self: MysqlQuery): string 101 +nim deleteByIdBuilder query_builder/models/mysql/query/mysql_builder.html#deleteByIdBuilder,MysqlQuery,int,string proc deleteByIdBuilder(self: MysqlQuery; id: int; key: string): string 113 +nim countBuilder query_builder/models/mysql/query/mysql_builder.html#countBuilder,MysqlQuery proc countBuilder(self: MysqlQuery): string 123 +nim maxBuilder query_builder/models/mysql/query/mysql_builder.html#maxBuilder,MysqlQuery,string proc maxBuilder(self: MysqlQuery; column: string): string 143 +nim minBuilder query_builder/models/mysql/query/mysql_builder.html#minBuilder,MysqlQuery,string proc minBuilder(self: MysqlQuery; column: string): string 163 +nim avgBuilder query_builder/models/mysql/query/mysql_builder.html#avgBuilder,MysqlQuery,string proc avgBuilder(self: MysqlQuery; column: string): string 183 +nim sumBuilder query_builder/models/mysql/query/mysql_builder.html#sumBuilder,MysqlQuery,string proc sumBuilder(self: MysqlQuery; column: string): string 203 +nim columnBuilder query_builder/models/mysql/query/mysql_builder.html#columnBuilder,MysqlQuery proc columnBuilder(self: MysqlQuery): string 223 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.html b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.html new file mode 100644 index 0000000..9dc8ec2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.html @@ -0,0 +1,610 @@ + + + + + + + +src/allographer/query_builder/models/mysql/query/mysql_generator + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/mysql/query/mysql_generator

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc deleteByIdSql(self: MysqlQuery; id: int; key: string): MysqlQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteSql(self: MysqlQuery): MysqlQuery {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBySql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc havingSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueSql(self: MysqlQuery; items: JsonNode): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesSql(self: MysqlQuery; rows: openArray[JsonNode]): MysqlQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc joinSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoinSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limitSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offsetSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBySql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhereSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectAvgSql(self: MysqlQuery; column: string): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectByIdSql(self: MysqlQuery; key: string): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectCountSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstSql(self: MysqlQuery): MysqlQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMaxSql(self: MysqlQuery; column: string): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMinSql(self: MysqlQuery; column: string): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSumSql(self: MysqlQuery; column: string): MysqlQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateValuesSql(self: MysqlQuery; items: JsonNode): MysqlQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenStringSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereInSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenStringSql(self: MysqlQuery): MysqlQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotInSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNullSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereSql(self: MysqlQuery): MysqlQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.idx b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.idx new file mode 100644 index 0000000..3b22fed --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/mysql/query/mysql_generator.idx @@ -0,0 +1,33 @@ +nimTitle mysql_generator query_builder/models/mysql/query/mysql_generator.html module src/allographer/query_builder/models/mysql/query/mysql_generator 0 +nim selectSql query_builder/models/mysql/query/mysql_generator.html#selectSql,MysqlQuery proc selectSql(self: MysqlQuery): MysqlQuery 38 +nim fromSql query_builder/models/mysql/query/mysql_generator.html#fromSql,MysqlQuery proc fromSql(self: MysqlQuery): MysqlQuery 65 +nim selectFirstSql query_builder/models/mysql/query/mysql_generator.html#selectFirstSql,MysqlQuery proc selectFirstSql(self: MysqlQuery): MysqlQuery 71 +nim selectByIdSql query_builder/models/mysql/query/mysql_generator.html#selectByIdSql,MysqlQuery,string proc selectByIdSql(self: MysqlQuery; key: string): MysqlQuery 76 +nim joinSql query_builder/models/mysql/query/mysql_generator.html#joinSql,MysqlQuery proc joinSql(self: MysqlQuery): MysqlQuery 85 +nim leftJoinSql query_builder/models/mysql/query/mysql_generator.html#leftJoinSql,MysqlQuery proc leftJoinSql(self: MysqlQuery): MysqlQuery 97 +nim whereSql query_builder/models/mysql/query/mysql_generator.html#whereSql,MysqlQuery proc whereSql(self: MysqlQuery): MysqlQuery 109 +nim orWhereSql query_builder/models/mysql/query/mysql_generator.html#orWhereSql,MysqlQuery proc orWhereSql(self: MysqlQuery): MysqlQuery 122 +nim whereBetweenSql query_builder/models/mysql/query/mysql_generator.html#whereBetweenSql,MysqlQuery proc whereBetweenSql(self: MysqlQuery): MysqlQuery 136 +nim whereBetweenStringSql query_builder/models/mysql/query/mysql_generator.html#whereBetweenStringSql,MysqlQuery proc whereBetweenStringSql(self: MysqlQuery): MysqlQuery 152 +nim whereNotBetweenSql query_builder/models/mysql/query/mysql_generator.html#whereNotBetweenSql,MysqlQuery proc whereNotBetweenSql(self: MysqlQuery): MysqlQuery 168 +nim whereNotBetweenStringSql query_builder/models/mysql/query/mysql_generator.html#whereNotBetweenStringSql,MysqlQuery proc whereNotBetweenStringSql(self: MysqlQuery): MysqlQuery 184 +nim whereInSql query_builder/models/mysql/query/mysql_generator.html#whereInSql,MysqlQuery proc whereInSql(self: MysqlQuery): MysqlQuery 200 +nim whereNotInSql query_builder/models/mysql/query/mysql_generator.html#whereNotInSql,MysqlQuery proc whereNotInSql(self: MysqlQuery): MysqlQuery 216 +nim whereNullSql query_builder/models/mysql/query/mysql_generator.html#whereNullSql,MysqlQuery proc whereNullSql(self: MysqlQuery): MysqlQuery 232 +nim groupBySql query_builder/models/mysql/query/mysql_generator.html#groupBySql,MysqlQuery proc groupBySql(self: MysqlQuery): MysqlQuery 244 +nim havingSql query_builder/models/mysql/query/mysql_generator.html#havingSql,MysqlQuery proc havingSql(self: MysqlQuery): MysqlQuery 255 +nim orderBySql query_builder/models/mysql/query/mysql_generator.html#orderBySql,MysqlQuery proc orderBySql(self: MysqlQuery): MysqlQuery 270 +nim limitSql query_builder/models/mysql/query/mysql_generator.html#limitSql,MysqlQuery proc limitSql(self: MysqlQuery): MysqlQuery 283 +nim offsetSql query_builder/models/mysql/query/mysql_generator.html#offsetSql,MysqlQuery proc offsetSql(self: MysqlQuery): MysqlQuery 291 +nim insertSql query_builder/models/mysql/query/mysql_generator.html#insertSql,MysqlQuery proc insertSql(self: MysqlQuery): MysqlQuery 301 +nim insertValueSql query_builder/models/mysql/query/mysql_generator.html#insertValueSql,MysqlQuery,JsonNode proc insertValueSql(self: MysqlQuery; items: JsonNode): MysqlQuery 307 +nim insertValuesSql query_builder/models/mysql/query/mysql_generator.html#insertValuesSql,MysqlQuery,openArray[JsonNode] proc insertValuesSql(self: MysqlQuery; rows: openArray[JsonNode]): MysqlQuery 327 +nim updateSql query_builder/models/mysql/query/mysql_generator.html#updateSql,MysqlQuery proc updateSql(self: MysqlQuery): MysqlQuery 359 +nim updateValuesSql query_builder/models/mysql/query/mysql_generator.html#updateValuesSql,MysqlQuery,JsonNode proc updateValuesSql(self: MysqlQuery; items: JsonNode): MysqlQuery 369 +nim deleteSql query_builder/models/mysql/query/mysql_generator.html#deleteSql,MysqlQuery proc deleteSql(self: MysqlQuery): MysqlQuery 391 +nim deleteByIdSql query_builder/models/mysql/query/mysql_generator.html#deleteByIdSql,MysqlQuery,int,string proc deleteByIdSql(self: MysqlQuery; id: int; key: string): MysqlQuery 396 +nim selectCountSql query_builder/models/mysql/query/mysql_generator.html#selectCountSql,MysqlQuery proc selectCountSql(self: MysqlQuery): MysqlQuery 402 +nim selectMaxSql query_builder/models/mysql/query/mysql_generator.html#selectMaxSql,MysqlQuery,string proc selectMaxSql(self: MysqlQuery; column: string): MysqlQuery 413 +nim selectMinSql query_builder/models/mysql/query/mysql_generator.html#selectMinSql,MysqlQuery,string proc selectMinSql(self: MysqlQuery; column: string): MysqlQuery 418 +nim selectAvgSql query_builder/models/mysql/query/mysql_generator.html#selectAvgSql,MysqlQuery,string proc selectAvgSql(self: MysqlQuery; column: string): MysqlQuery 423 +nim selectSumSql query_builder/models/mysql/query/mysql_generator.html#selectSumSql,MysqlQuery,string proc selectSumSql(self: MysqlQuery; column: string): MysqlQuery 428 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/orm.html b/clients/nim-allographer/docs/v2/query_builder/models/orm.html new file mode 100644 index 0000000..f093394 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/orm.html @@ -0,0 +1,116 @@ + + + + + + + +src/allographer/query_builder/models/orm + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/orm

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Procs

+
+
+
+
proc orm[T](response: Future[Option[JsonNode]]; typ: typedesc[T]): Future[
+    Option[T]] {....stackTrace: false.}
+
+ + + +
+
+
+
proc orm[T](response: Future[seq[JsonNode]]; typ: typedesc[T]): Future[seq[T]] {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/orm.idx b/clients/nim-allographer/docs/v2/query_builder/models/orm.idx new file mode 100644 index 0000000..835b6c9 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/orm.idx @@ -0,0 +1,4 @@ +nimTitle orm query_builder/models/orm.html module src/allographer/query_builder/models/orm 0 +nim orm query_builder/models/orm.html#orm,Future[seq[JsonNode]],typedesc[T] proc orm[T](response: Future[seq[JsonNode]]; typ: typedesc[T]): Future[seq[T]] 5 +nim orm query_builder/models/orm.html#orm,Future[Option[JsonNode]],typedesc[T] proc orm[T](response: Future[Option[JsonNode]]; typ: typedesc[T]): Future[Option[T]] 13 +nimgrp orm query_builder/models/orm.html#orm-procs-all proc 5 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.html b/clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.html new file mode 100644 index 0000000..a33fa08 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.html @@ -0,0 +1,114 @@ + + + + + + + +src/allographer/query_builder/models/postgres/poatgres_transaction + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/postgres/poatgres_transaction

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Templates

+
+
+
+
template transaction(rdb: PostgresConnections; callback: untyped)
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.idx b/clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.idx new file mode 100644 index 0000000..cb9ce1a --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/poatgres_transaction.idx @@ -0,0 +1,2 @@ +nimTitle poatgres_transaction query_builder/models/postgres/poatgres_transaction.html module src/allographer/query_builder/models/postgres/poatgres_transaction 0 +nim transaction query_builder/models/postgres/poatgres_transaction.html#transaction.t,PostgresConnections,untyped template transaction(rdb: PostgresConnections; callback: untyped) 41 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.html b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.html new file mode 100644 index 0000000..314b21e --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.html @@ -0,0 +1,677 @@ + + + + + + + +src/allographer/query_builder/models/postgres/postgres_exec + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/postgres/postgres_exec

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avg(self: PostgresQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc begin(self: PostgresConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, KeyError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columns(self: PostgresQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + get columns sequence from table + +
+
+ +
+
+
+
proc commit(self: PostgresConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, KeyError, DbError, ValueError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc count(self: PostgresQuery): Future[int] {....stackTrace: false, raises: [
+    Exception, ValueError, KeyError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc delete(self: PostgresQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc delete(self: PostgresQuery; id: int; key = "id"): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc exec(self: RawPostgresQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc find(self: PostgresQuery; id: int; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc find(self: PostgresQuery; id: string; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc findPlain(self: PostgresQuery; id: int; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc findPlain(self: PostgresQuery; id: string; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc first(self: PostgresQuery): Future[Option[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc first(self: RawPostgresQuery): Future[Option[JsonNode]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, OSError, IOError,
+                                DbError, JsonParsingError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc firstPlain(self: PostgresQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc firstPlain(self: RawPostgresQuery): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc get(self: PostgresQuery): Future[seq[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc get(self: RawPostgresQuery): Future[seq[JsonNode]] {....stackTrace: false, raises: [
+    Exception, ValueError, OSError, IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc getPlain(self: PostgresQuery): Future[seq[seq[string]]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc getPlain(self: RawPostgresQuery): Future[seq[seq[string]]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc insert(self: PostgresQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + items is JObject + +
+
+
+
proc insert(self: PostgresQuery; items: seq[JsonNode]): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insert[T](self: PostgresQuery; items: seq[T]): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+
+
proc insert[T](self: PostgresQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc insertId(self: PostgresQuery; items: JsonNode; key = "id"): Future[string] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insertId(self: PostgresQuery; items: seq[JsonNode]; key = "id"): Future[
+    seq[string]] {....stackTrace: false, raises: [Exception, ValueError, KeyError,
+    OSError, IOError, DbError], tags: [RootEffect, WriteDirEffect,
+                                       ReadDirEffect, ReadIOEffect,
+                                       WriteIOEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc insertId[T](self: PostgresQuery; items: seq[T]; key = "id"): Future[
+    seq[string]] {....stackTrace: false.}
+
+ + + +
+
+
+
proc insertId[T](self: PostgresQuery; items: T; key = "id"): Future[string] {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc max(self: PostgresQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc min(self: PostgresQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rollback(self: PostgresConnections): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, OSError, IOError, KeyError, DbError, ValueError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sum(self: PostgresQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, JsonParsingError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc update(self: PostgresQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+
+
proc update[T](self: PostgresQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+ +
+
+
+

Templates

+
+
+
+
template seeder(rdb: PostgresConnections; tableName, column: string;
+                body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table or specified column is empty. + +
+
+
+
template seeder(rdb: PostgresConnections; tableName: string; body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table is empty. + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.idx b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.idx new file mode 100644 index 0000000..d878620 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_exec.idx @@ -0,0 +1,48 @@ +nimTitle postgres_exec query_builder/models/postgres/postgres_exec.html module src/allographer/query_builder/models/postgres/postgres_exec 0 +nim get query_builder/models/postgres/postgres_exec.html#get,PostgresQuery proc get(self: PostgresQuery): Future[seq[JsonNode]] 419 +nim first query_builder/models/postgres/postgres_exec.html#first,PostgresQuery proc first(self: PostgresQuery): Future[Option[JsonNode]] 431 +nim find query_builder/models/postgres/postgres_exec.html#find,PostgresQuery,string,string proc find(self: PostgresQuery; id: string; key = "id"): Future[Option[JsonNode]] 443 +nim find query_builder/models/postgres/postgres_exec.html#find,PostgresQuery,int,string proc find(self: PostgresQuery; id: int; key = "id"): Future[Option[JsonNode]] 456 +nim getPlain query_builder/models/postgres/postgres_exec.html#getPlain,PostgresQuery proc getPlain(self: PostgresQuery): Future[seq[seq[string]]] 461 +nim firstPlain query_builder/models/postgres/postgres_exec.html#firstPlain,PostgresQuery proc firstPlain(self: PostgresQuery): Future[seq[string]] 473 +nim findPlain query_builder/models/postgres/postgres_exec.html#findPlain,PostgresQuery,string,string proc findPlain(self: PostgresQuery; id: string; key = "id"): Future[seq[string]] 485 +nim findPlain query_builder/models/postgres/postgres_exec.html#findPlain,PostgresQuery,int,string proc findPlain(self: PostgresQuery; id: int; key = "id"): Future[seq[string]] 498 +nim insert query_builder/models/postgres/postgres_exec.html#insert,PostgresQuery,JsonNode proc insert(self: PostgresQuery; items: JsonNode): owned(Future[void]) 503 +nim insert query_builder/models/postgres/postgres_exec.html#insert,PostgresQuery,seq[JsonNode] proc insert(self: PostgresQuery; items: seq[JsonNode]): owned(Future[void]) 511 +nim insertId query_builder/models/postgres/postgres_exec.html#insertId,PostgresQuery,JsonNode,string proc insertId(self: PostgresQuery; items: JsonNode; key = "id"): Future[string] 518 +nim insertId query_builder/models/postgres/postgres_exec.html#insertId,PostgresQuery,seq[JsonNode],string proc insertId(self: PostgresQuery; items: seq[JsonNode]; key = "id"): Future[\n seq[string]] 526 +nim insert query_builder/models/postgres/postgres_exec.html#insert,PostgresQuery,T proc insert[T](self: PostgresQuery; items: T): owned(Future[void]) 538 +nim insert query_builder/models/postgres/postgres_exec.html#insert,PostgresQuery,seq[T] proc insert[T](self: PostgresQuery; items: seq[T]): owned(Future[void]) 545 +nim insertId query_builder/models/postgres/postgres_exec.html#insertId,PostgresQuery,T,string proc insertId[T](self: PostgresQuery; items: T; key = "id"): Future[string] 553 +nim insertId query_builder/models/postgres/postgres_exec.html#insertId,PostgresQuery,seq[T],string proc insertId[T](self: PostgresQuery; items: seq[T]; key = "id"): Future[seq[string]] 561 +nim update query_builder/models/postgres/postgres_exec.html#update,PostgresQuery,JsonNode proc update(self: PostgresQuery; items: JsonNode): owned(Future[void]) 573 +nim update query_builder/models/postgres/postgres_exec.html#update,PostgresQuery,T proc update[T](self: PostgresQuery; items: T): owned(Future[void]) 580 +nim delete query_builder/models/postgres/postgres_exec.html#delete,PostgresQuery proc delete(self: PostgresQuery): owned(Future[void]) 587 +nim delete query_builder/models/postgres/postgres_exec.html#delete,PostgresQuery,int,string proc delete(self: PostgresQuery; id: int; key = "id"): owned(Future[void]) 594 +nim columns query_builder/models/postgres/postgres_exec.html#columns,PostgresQuery proc columns(self: PostgresQuery): Future[seq[string]] 602 +nim count query_builder/models/postgres/postgres_exec.html#count,PostgresQuery proc count(self: PostgresQuery): Future[int] 615 +nim min query_builder/models/postgres/postgres_exec.html#min,PostgresQuery,string proc min(self: PostgresQuery; column: string): Future[Option[string]] 627 +nim max query_builder/models/postgres/postgres_exec.html#max,PostgresQuery,string proc max(self: PostgresQuery; column: string): Future[Option[string]] 644 +nim avg query_builder/models/postgres/postgres_exec.html#avg,PostgresQuery,string proc avg(self: PostgresQuery; column: string): Future[Option[float]] 661 +nim sum query_builder/models/postgres/postgres_exec.html#sum,PostgresQuery,string proc sum(self: PostgresQuery; column: string): Future[Option[float]] 672 +nim begin query_builder/models/postgres/postgres_exec.html#begin,PostgresConnections proc begin(self: PostgresConnections): owned(Future[void]) 683 +nim rollback query_builder/models/postgres/postgres_exec.html#rollback,PostgresConnections proc rollback(self: PostgresConnections): owned(Future[void]) 688 +nim commit query_builder/models/postgres/postgres_exec.html#commit,PostgresConnections proc commit(self: PostgresConnections): owned(Future[void]) 693 +nim get query_builder/models/postgres/postgres_exec.html#get,RawPostgresQuery proc get(self: RawPostgresQuery): Future[seq[JsonNode]] 698 +nim getPlain query_builder/models/postgres/postgres_exec.html#getPlain,RawPostgresQuery proc getPlain(self: RawPostgresQuery): Future[seq[seq[string]]] 704 +nim exec query_builder/models/postgres/postgres_exec.html#exec,RawPostgresQuery proc exec(self: RawPostgresQuery): owned(Future[void]) 710 +nim first query_builder/models/postgres/postgres_exec.html#first,RawPostgresQuery proc first(self: RawPostgresQuery): Future[Option[JsonNode]] 716 +nim firstPlain query_builder/models/postgres/postgres_exec.html#firstPlain,RawPostgresQuery proc firstPlain(self: RawPostgresQuery): Future[seq[string]] 722 +nim seeder query_builder/models/postgres/postgres_exec.html#seeder.t,PostgresConnections,string,untyped template seeder(rdb: PostgresConnections; tableName: string; body: untyped): untyped 728 +nim seeder query_builder/models/postgres/postgres_exec.html#seeder.t,PostgresConnections,string,string,untyped template seeder(rdb: PostgresConnections; tableName, column: string; body: untyped): untyped 735 +nimgrp getplain query_builder/models/postgres/postgres_exec.html#getPlain-procs-all proc 461 +nimgrp firstplain query_builder/models/postgres/postgres_exec.html#firstPlain-procs-all proc 473 +nimgrp findplain query_builder/models/postgres/postgres_exec.html#findPlain-procs-all proc 485 +nimgrp insertid query_builder/models/postgres/postgres_exec.html#insertId-procs-all proc 518 +nimgrp delete query_builder/models/postgres/postgres_exec.html#delete-procs-all proc 587 +nimgrp get query_builder/models/postgres/postgres_exec.html#get-procs-all proc 419 +nimgrp update query_builder/models/postgres/postgres_exec.html#update-procs-all proc 573 +nimgrp find query_builder/models/postgres/postgres_exec.html#find-procs-all proc 443 +nimgrp first query_builder/models/postgres/postgres_exec.html#first-procs-all proc 431 +nimgrp insert query_builder/models/postgres/postgres_exec.html#insert-procs-all proc 503 +nimgrp seeder query_builder/models/postgres/postgres_exec.html#seeder-templates-all template 728 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.html b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.html new file mode 100644 index 0000000..6f7c1d2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.html @@ -0,0 +1,149 @@ + + + + + + + +src/allographer/query_builder/models/postgres/postgres_open + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/postgres/postgres_open

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dbOpen(_: type PostgreSQL; database: string; user: string;
+            password: string; host: string; port: int; maxConnections = 1;
+            timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;
+            logDir = ""): PostgresConnections
+
+ + + +
+
+
+
proc dbOpen(_: type PostgreSQL; databaseUrl: DatabaseUrl;
+            maxConnections: int = 1; timeout = 30; shouldDisplayLog = false;
+            shouldOutputLogFile = false; logDir = ""): PostgresConnections
+
+ + + +
+
+
+
proc dbOpen(_: type PostgreSQL; url: string; maxConnections: int = 1;
+            timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;
+            logDir = ""): PostgresConnections
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.idx b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.idx new file mode 100644 index 0000000..a315f5b --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_open.idx @@ -0,0 +1,5 @@ +nimTitle postgres_open query_builder/models/postgres/postgres_open.html module src/allographer/query_builder/models/postgres/postgres_open 0 +nim dbOpen query_builder/models/postgres/postgres_open.html#dbOpen,typePostgreSQL,string,string,string,string,int,int,int,string proc dbOpen(_: type PostgreSQL; database: string; user: string; password: string;\n host: string; port: int; maxConnections = 1; timeout = 30;\n shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): PostgresConnections 13 +nim dbOpen query_builder/models/postgres/postgres_open.html#dbOpen,typePostgreSQL,string,int,int,string proc dbOpen(_: type PostgreSQL; url: string; maxConnections: int = 1; timeout = 30;\n shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): PostgresConnections 41 +nim dbOpen query_builder/models/postgres/postgres_open.html#dbOpen,typePostgreSQL,DatabaseUrl,int,int,string proc dbOpen(_: type PostgreSQL; databaseUrl: DatabaseUrl; maxConnections: int = 1;\n timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;\n logDir = ""): PostgresConnections 46 +nimgrp dbopen query_builder/models/postgres/postgres_open.html#dbOpen-procs-all proc 13 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.html b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.html new file mode 100644 index 0000000..abe237d --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.html @@ -0,0 +1,475 @@ + + + + + + + +src/allographer/query_builder/models/postgres/postgres_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/postgres/postgres_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc `distinct`(self: PostgresQuery): PostgresQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBy(self: PostgresQuery; column: string): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc having(self: PostgresQuery; column: string; symbol: string;
+            value: string | int | float | bool): PostgresQuery
+
+ + + +
+
+
+
proc having(self: PostgresQuery; column: string; symbol: string; value: nil.type): PostgresQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc join(self: PostgresQuery; table: string; column1: string; symbol: string;
+          column2: string): PostgresQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoin(self: PostgresQuery; table: string; column1: string;
+              symbol: string; column2: string): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limit(self: PostgresQuery; num: int): PostgresQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offset(self: PostgresQuery; num: int): PostgresQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBy(self: PostgresQuery; column: string; order: Order): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhere(self: PostgresQuery; column: string; symbol: string;
+             value: string | int | float | bool): PostgresQuery
+
+ + + +
+
+
+
proc orWhere(self: PostgresQuery; column: string; symbol: string;
+             value: nil.type): PostgresQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc raw(self: PostgresConnections; sql: string; arges = newJArray()): RawPostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ +

arges is JArray

+

can't use BLOB data.

+ + +
+
+ +
+
+
+
proc select(self: PostgresConnections; columnsArg: varargs[string]): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc table(self: PostgresConnections; tableArg: string): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc table(self: PostgresQuery; tableArg: string): PostgresQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc where(self: PostgresQuery; column: string; symbol: string;
+           value: string | int | float): PostgresQuery
+
+ + + +
+
+
+
proc where(self: PostgresQuery; column: string; symbol: string; value: bool): PostgresQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc where(self: PostgresQuery; column: string; symbol: string; value: nil.type): PostgresQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetween(self: PostgresQuery; column: string;
+                  width: array[2, int | float]): PostgresQuery
+
+ + + +
+
+
+
proc whereBetween(self: PostgresQuery; column: string; width: array[2, string]): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereIn(self: PostgresQuery; column: string;
+             width: seq[int | float | string]): PostgresQuery
+
+ + + +
+
+ +
+
+
+
proc whereNotBetween(self: PostgresQuery; column: string;
+                     width: array[2, int | float]): PostgresQuery
+
+ + + +
+
+
+
proc whereNotBetween(self: PostgresQuery; column: string;
+                     width: array[2, string]): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotIn(self: PostgresQuery; column: string;
+                width: seq[int | float | string]): PostgresQuery
+
+ + + +
+
+ +
+
+
+
proc whereNull(self: PostgresQuery; column: string): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.idx b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.idx new file mode 100644 index 0000000..6d5a6e2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_query.idx @@ -0,0 +1,32 @@ +nimTitle postgres_query query_builder/models/postgres/postgres_query.html module src/allographer/query_builder/models/postgres/postgres_query 0 +nim select query_builder/models/postgres/postgres_query.html#select,PostgresConnections,varargs[string] proc select(self: PostgresConnections; columnsArg: varargs[string]): PostgresQuery 15 +nim table query_builder/models/postgres/postgres_query.html#table,PostgresConnections,string proc table(self: PostgresConnections; tableArg: string): PostgresQuery 35 +nim table query_builder/models/postgres/postgres_query.html#table,PostgresQuery,string proc table(self: PostgresQuery; tableArg: string): PostgresQuery 51 +nim `distinct` query_builder/models/postgres/postgres_query.html#distinct,PostgresQuery proc `distinct`(self: PostgresQuery): PostgresQuery 56 +nim join query_builder/models/postgres/postgres_query.html#join,PostgresQuery,string,string,string,string proc join(self: PostgresQuery; table: string; column1: string; symbol: string;\n column2: string): PostgresQuery 62 +nim leftJoin query_builder/models/postgres/postgres_query.html#leftJoin,PostgresQuery,string,string,string,string proc leftJoin(self: PostgresQuery; table: string; column1: string; symbol: string;\n column2: string): PostgresQuery 81 +nim where query_builder/models/postgres/postgres_query.html#where,PostgresQuery,string,string, proc where(self: PostgresQuery; column: string; symbol: string;\n value: string | int | float): PostgresQuery 103 +nim where query_builder/models/postgres/postgres_query.html#where,PostgresQuery,string,string,bool proc where(self: PostgresQuery; column: string; symbol: string; value: bool): PostgresQuery 130 +nim where query_builder/models/postgres/postgres_query.html#where,PostgresQuery,string,string,_2 proc where(self: PostgresQuery; column: string; symbol: string; value: nil.type): PostgresQuery 157 +nim orWhere query_builder/models/postgres/postgres_query.html#orWhere,PostgresQuery,string,string, proc orWhere(self: PostgresQuery; column: string; symbol: string;\n value: string | int | float | bool): PostgresQuery 181 +nim orWhere query_builder/models/postgres/postgres_query.html#orWhere,PostgresQuery,string,string,_2 proc orWhere(self: PostgresQuery; column: string; symbol: string; value: nil.type): PostgresQuery 208 +nim whereBetween query_builder/models/postgres/postgres_query.html#whereBetween,PostgresQuery,string,array[,] proc whereBetween(self: PostgresQuery; column: string; width: array[2, int | float]): PostgresQuery 230 +nim whereBetween query_builder/models/postgres/postgres_query.html#whereBetween,PostgresQuery,string,array[,string] proc whereBetween(self: PostgresQuery; column: string; width: array[2, string]): PostgresQuery 247 +nim whereNotBetween query_builder/models/postgres/postgres_query.html#whereNotBetween,PostgresQuery,string,array[,] proc whereNotBetween(self: PostgresQuery; column: string;\n width: array[2, int | float]): PostgresQuery 264 +nim whereNotBetween query_builder/models/postgres/postgres_query.html#whereNotBetween,PostgresQuery,string,array[,string] proc whereNotBetween(self: PostgresQuery; column: string; width: array[2, string]): PostgresQuery 281 +nim whereIn query_builder/models/postgres/postgres_query.html#whereIn,PostgresQuery,string,seq[] proc whereIn(self: PostgresQuery; column: string; width: seq[int | float | string]): PostgresQuery 298 +nim whereNotIn query_builder/models/postgres/postgres_query.html#whereNotIn,PostgresQuery,string,seq[] proc whereNotIn(self: PostgresQuery; column: string; width: seq[int | float | string]): PostgresQuery 315 +nim whereNull query_builder/models/postgres/postgres_query.html#whereNull,PostgresQuery,string proc whereNull(self: PostgresQuery; column: string): PostgresQuery 332 +nim groupBy query_builder/models/postgres/postgres_query.html#groupBy,PostgresQuery,string proc groupBy(self: PostgresQuery; column: string): PostgresQuery 346 +nim having query_builder/models/postgres/postgres_query.html#having,PostgresQuery,string,string, proc having(self: PostgresQuery; column: string; symbol: string;\n value: string | int | float | bool): PostgresQuery 354 +nim having query_builder/models/postgres/postgres_query.html#having,PostgresQuery,string,string,_2 proc having(self: PostgresQuery; column: string; symbol: string; value: nil.type): PostgresQuery 380 +nim orderBy query_builder/models/postgres/postgres_query.html#orderBy,PostgresQuery,string,Order proc orderBy(self: PostgresQuery; column: string; order: Order): PostgresQuery 404 +nim limit query_builder/models/postgres/postgres_query.html#limit,PostgresQuery,int proc limit(self: PostgresQuery; num: int): PostgresQuery 418 +nim offset query_builder/models/postgres/postgres_query.html#offset,PostgresQuery,int proc offset(self: PostgresQuery; num: int): PostgresQuery 423 +nim raw query_builder/models/postgres/postgres_query.html#raw,PostgresConnections,string proc raw(self: PostgresConnections; sql: string; arges = newJArray()): RawPostgresQuery 428 +nimgrp where query_builder/models/postgres/postgres_query.html#where-procs-all proc 103 +nimgrp wherebetween query_builder/models/postgres/postgres_query.html#whereBetween-procs-all proc 230 +nimgrp wherenotbetween query_builder/models/postgres/postgres_query.html#whereNotBetween-procs-all proc 264 +nimgrp orwhere query_builder/models/postgres/postgres_query.html#orWhere-procs-all proc 181 +nimgrp having query_builder/models/postgres/postgres_query.html#having-procs-all proc 354 +nimgrp table query_builder/models/postgres/postgres_query.html#table-procs-all proc 35 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.html b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.html new file mode 100644 index 0000000..9ae89ca --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.html @@ -0,0 +1,247 @@ + + + + + + + +src/allographer/query_builder/models/postgres/postgres_types + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/postgres/postgres_types

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

created by let rdb = dbOpen(PostgreSQL, "localhost", 5432)created by rdb.select("columnName") or rdb.table("tableName")

+ +
+

Types

+
+
+
Connection = ref object
+  conn*: PPGconn
+  isBusy*: bool
+  createdAt*: int64
+
+ + + +
+
+
+
Connections = ref object
+  conns*: seq[Connection]
+  timeout*: int              ## `getFreeConn` が接続を待つときに積む Future。`returnConn` が先頭から 1 件だけ完了させる。
+  waiters*: Deque[Future[void]] ## `exec` / `insertId` 用。テーブルごとに information_schema 相当の列型を初回のみ取得して保持する。
+  columnTypeCache*: Table[string, seq[Row]]
+
+ + + +
+
+
+
PostgresConnections = ref object
+  log*: LogSetting
+  pools*: Connections
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
PostgreSQL = object
+
+ + + +
+
+
+
PostgresQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
RawPostgresQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc `$`(self: PostgresConnections | PostgresQuery | RawPostgresQuery): string
+
+ + + +
+
+ +
+
+
+
proc isConnected(self: PostgresConnections | PostgresQuery | RawPostgresQuery): bool
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.idx b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.idx new file mode 100644 index 0000000..0fb7a00 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/postgres_types.idx @@ -0,0 +1,9 @@ +nimTitle postgres_types query_builder/models/postgres/postgres_types.html module src/allographer/query_builder/models/postgres/postgres_types 0 +nim PostgreSQL query_builder/models/postgres/postgres_types.html#PostgreSQL object PostgreSQL 10 +nim Connection query_builder/models/postgres/postgres_types.html#Connection type Connection 13 +nim Connections query_builder/models/postgres/postgres_types.html#Connections type Connections 19 +nim PostgresConnections query_builder/models/postgres/postgres_types.html#PostgresConnections type PostgresConnections 29 +nim PostgresQuery query_builder/models/postgres/postgres_types.html#PostgresQuery type PostgresQuery 38 +nim RawPostgresQuery query_builder/models/postgres/postgres_types.html#RawPostgresQuery type RawPostgresQuery 49 +nim `$` query_builder/models/postgres/postgres_types.html#$ proc `$`(self: PostgresConnections | PostgresQuery | RawPostgresQuery): string 60 +nim isConnected query_builder/models/postgres/postgres_types.html#isConnected proc isConnected(self: PostgresConnections | PostgresQuery | RawPostgresQuery): bool 64 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.html b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.html new file mode 100644 index 0000000..23ba1c3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.html @@ -0,0 +1,323 @@ + + + + + + + +src/allographer/query_builder/models/postgres/query/postgres_builder + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/postgres/query/postgres_builder

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avgBuilder(self: PostgresQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columnBuilder(self: PostgresQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc countBuilder(self: PostgresQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteBuilder(self: PostgresQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteByIdBuilder(self: PostgresQuery; id: int; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueBuilder(self: PostgresQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesBuilder(self: PostgresQuery; rows: openArray[JsonNode]): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc maxBuilder(self: PostgresQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc minBuilder(self: PostgresQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectBuilder(self: PostgresQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFindBuilder(self: PostgresQuery; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstBuilder(self: PostgresQuery): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sumBuilder(self: PostgresQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateBuilder(self: PostgresQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.idx b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.idx new file mode 100644 index 0000000..98ed203 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_builder.idx @@ -0,0 +1,15 @@ +nimTitle postgres_builder query_builder/models/postgres/query/postgres_builder.html module src/allographer/query_builder/models/postgres/query/postgres_builder 0 +nim selectBuilder query_builder/models/postgres/query/postgres_builder.html#selectBuilder,PostgresQuery proc selectBuilder(self: PostgresQuery): string 8 +nim selectFirstBuilder query_builder/models/postgres/query/postgres_builder.html#selectFirstBuilder,PostgresQuery proc selectFirstBuilder(self: PostgresQuery): string 30 +nim selectFindBuilder query_builder/models/postgres/query/postgres_builder.html#selectFindBuilder,PostgresQuery,string proc selectFindBuilder(self: PostgresQuery; key: string): string 52 +nim insertValueBuilder query_builder/models/postgres/query/postgres_builder.html#insertValueBuilder,PostgresQuery,JsonNode proc insertValueBuilder(self: PostgresQuery; items: JsonNode): string 73 +nim insertValuesBuilder query_builder/models/postgres/query/postgres_builder.html#insertValuesBuilder,PostgresQuery,openArray[JsonNode] proc insertValuesBuilder(self: PostgresQuery; rows: openArray[JsonNode]): string 79 +nim updateBuilder query_builder/models/postgres/query/postgres_builder.html#updateBuilder,PostgresQuery,JsonNode proc updateBuilder(self: PostgresQuery; items: JsonNode): string 88 +nim deleteBuilder query_builder/models/postgres/query/postgres_builder.html#deleteBuilder,PostgresQuery proc deleteBuilder(self: PostgresQuery): string 101 +nim deleteByIdBuilder query_builder/models/postgres/query/postgres_builder.html#deleteByIdBuilder,PostgresQuery,int,string proc deleteByIdBuilder(self: PostgresQuery; id: int; key: string): string 113 +nim countBuilder query_builder/models/postgres/query/postgres_builder.html#countBuilder,PostgresQuery proc countBuilder(self: PostgresQuery): string 123 +nim maxBuilder query_builder/models/postgres/query/postgres_builder.html#maxBuilder,PostgresQuery,string proc maxBuilder(self: PostgresQuery; column: string): string 143 +nim minBuilder query_builder/models/postgres/query/postgres_builder.html#minBuilder,PostgresQuery,string proc minBuilder(self: PostgresQuery; column: string): string 163 +nim avgBuilder query_builder/models/postgres/query/postgres_builder.html#avgBuilder,PostgresQuery,string proc avgBuilder(self: PostgresQuery; column: string): string 183 +nim sumBuilder query_builder/models/postgres/query/postgres_builder.html#sumBuilder,PostgresQuery,string proc sumBuilder(self: PostgresQuery; column: string): string 203 +nim columnBuilder query_builder/models/postgres/query/postgres_builder.html#columnBuilder,PostgresQuery proc columnBuilder(self: PostgresQuery): string 223 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.html b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.html new file mode 100644 index 0000000..5db1718 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.html @@ -0,0 +1,611 @@ + + + + + + + +src/allographer/query_builder/models/postgres/query/postgres_generator + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/postgres/query/postgres_generator

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc deleteByIdSql(self: PostgresQuery; id: int; key: string): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteSql(self: PostgresQuery): PostgresQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBySql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc havingSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueSql(self: PostgresQuery; items: JsonNode): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesSql(self: PostgresQuery; rows: openArray[JsonNode]): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc joinSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoinSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limitSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offsetSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBySql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhereSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectAvgSql(self: PostgresQuery; column: string): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectByIdSql(self: PostgresQuery; key: string): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectCountSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstSql(self: PostgresQuery): PostgresQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMaxSql(self: PostgresQuery; column: string): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMinSql(self: PostgresQuery; column: string): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSumSql(self: PostgresQuery; column: string): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateValuesSql(self: PostgresQuery; items: JsonNode): PostgresQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenStringSql(self: PostgresQuery): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereInSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenSql(self: PostgresQuery): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenStringSql(self: PostgresQuery): PostgresQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotInSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNullSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereSql(self: PostgresQuery): PostgresQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.idx b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.idx new file mode 100644 index 0000000..ca2e556 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/postgres/query/postgres_generator.idx @@ -0,0 +1,33 @@ +nimTitle postgres_generator query_builder/models/postgres/query/postgres_generator.html module src/allographer/query_builder/models/postgres/query/postgres_generator 0 +nim selectSql query_builder/models/postgres/query/postgres_generator.html#selectSql,PostgresQuery proc selectSql(self: PostgresQuery): PostgresQuery 43 +nim fromSql query_builder/models/postgres/query/postgres_generator.html#fromSql,PostgresQuery proc fromSql(self: PostgresQuery): PostgresQuery 64 +nim selectFirstSql query_builder/models/postgres/query/postgres_generator.html#selectFirstSql,PostgresQuery proc selectFirstSql(self: PostgresQuery): PostgresQuery 70 +nim selectByIdSql query_builder/models/postgres/query/postgres_generator.html#selectByIdSql,PostgresQuery,string proc selectByIdSql(self: PostgresQuery; key: string): PostgresQuery 75 +nim joinSql query_builder/models/postgres/query/postgres_generator.html#joinSql,PostgresQuery proc joinSql(self: PostgresQuery): PostgresQuery 84 +nim leftJoinSql query_builder/models/postgres/query/postgres_generator.html#leftJoinSql,PostgresQuery proc leftJoinSql(self: PostgresQuery): PostgresQuery 96 +nim whereSql query_builder/models/postgres/query/postgres_generator.html#whereSql,PostgresQuery proc whereSql(self: PostgresQuery): PostgresQuery 108 +nim orWhereSql query_builder/models/postgres/query/postgres_generator.html#orWhereSql,PostgresQuery proc orWhereSql(self: PostgresQuery): PostgresQuery 121 +nim whereBetweenSql query_builder/models/postgres/query/postgres_generator.html#whereBetweenSql,PostgresQuery proc whereBetweenSql(self: PostgresQuery): PostgresQuery 135 +nim whereBetweenStringSql query_builder/models/postgres/query/postgres_generator.html#whereBetweenStringSql,PostgresQuery proc whereBetweenStringSql(self: PostgresQuery): PostgresQuery 151 +nim whereNotBetweenSql query_builder/models/postgres/query/postgres_generator.html#whereNotBetweenSql,PostgresQuery proc whereNotBetweenSql(self: PostgresQuery): PostgresQuery 167 +nim whereNotBetweenStringSql query_builder/models/postgres/query/postgres_generator.html#whereNotBetweenStringSql,PostgresQuery proc whereNotBetweenStringSql(self: PostgresQuery): PostgresQuery 183 +nim whereInSql query_builder/models/postgres/query/postgres_generator.html#whereInSql,PostgresQuery proc whereInSql(self: PostgresQuery): PostgresQuery 199 +nim whereNotInSql query_builder/models/postgres/query/postgres_generator.html#whereNotInSql,PostgresQuery proc whereNotInSql(self: PostgresQuery): PostgresQuery 215 +nim whereNullSql query_builder/models/postgres/query/postgres_generator.html#whereNullSql,PostgresQuery proc whereNullSql(self: PostgresQuery): PostgresQuery 231 +nim groupBySql query_builder/models/postgres/query/postgres_generator.html#groupBySql,PostgresQuery proc groupBySql(self: PostgresQuery): PostgresQuery 243 +nim havingSql query_builder/models/postgres/query/postgres_generator.html#havingSql,PostgresQuery proc havingSql(self: PostgresQuery): PostgresQuery 254 +nim orderBySql query_builder/models/postgres/query/postgres_generator.html#orderBySql,PostgresQuery proc orderBySql(self: PostgresQuery): PostgresQuery 269 +nim limitSql query_builder/models/postgres/query/postgres_generator.html#limitSql,PostgresQuery proc limitSql(self: PostgresQuery): PostgresQuery 282 +nim offsetSql query_builder/models/postgres/query/postgres_generator.html#offsetSql,PostgresQuery proc offsetSql(self: PostgresQuery): PostgresQuery 290 +nim insertSql query_builder/models/postgres/query/postgres_generator.html#insertSql,PostgresQuery proc insertSql(self: PostgresQuery): PostgresQuery 300 +nim insertValueSql query_builder/models/postgres/query/postgres_generator.html#insertValueSql,PostgresQuery,JsonNode proc insertValueSql(self: PostgresQuery; items: JsonNode): PostgresQuery 306 +nim insertValuesSql query_builder/models/postgres/query/postgres_generator.html#insertValuesSql,PostgresQuery,openArray[JsonNode] proc insertValuesSql(self: PostgresQuery; rows: openArray[JsonNode]): PostgresQuery 326 +nim updateSql query_builder/models/postgres/query/postgres_generator.html#updateSql,PostgresQuery proc updateSql(self: PostgresQuery): PostgresQuery 358 +nim updateValuesSql query_builder/models/postgres/query/postgres_generator.html#updateValuesSql,PostgresQuery,JsonNode proc updateValuesSql(self: PostgresQuery; items: JsonNode): PostgresQuery 368 +nim deleteSql query_builder/models/postgres/query/postgres_generator.html#deleteSql,PostgresQuery proc deleteSql(self: PostgresQuery): PostgresQuery 390 +nim deleteByIdSql query_builder/models/postgres/query/postgres_generator.html#deleteByIdSql,PostgresQuery,int,string proc deleteByIdSql(self: PostgresQuery; id: int; key: string): PostgresQuery 395 +nim selectCountSql query_builder/models/postgres/query/postgres_generator.html#selectCountSql,PostgresQuery proc selectCountSql(self: PostgresQuery): PostgresQuery 401 +nim selectMaxSql query_builder/models/postgres/query/postgres_generator.html#selectMaxSql,PostgresQuery,string proc selectMaxSql(self: PostgresQuery; column: string): PostgresQuery 412 +nim selectMinSql query_builder/models/postgres/query/postgres_generator.html#selectMinSql,PostgresQuery,string proc selectMinSql(self: PostgresQuery; column: string): PostgresQuery 417 +nim selectAvgSql query_builder/models/postgres/query/postgres_generator.html#selectAvgSql,PostgresQuery,string proc selectAvgSql(self: PostgresQuery; column: string): PostgresQuery 422 +nim selectSumSql query_builder/models/postgres/query/postgres_generator.html#selectSumSql,PostgresQuery,string proc selectSumSql(self: PostgresQuery; column: string): PostgresQuery 427 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.html b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.html new file mode 100644 index 0000000..b32af50 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.html @@ -0,0 +1,323 @@ + + + + + + + +src/allographer/query_builder/models/sqlite/query/sqlite_builder + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/sqlite/query/sqlite_builder

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avgBuilder(self: SqliteQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columnBuilder(self: SqliteQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc countBuilder(self: SqliteQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteBuilder(self: SqliteQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteByIdBuilder(self: SqliteQuery; id: int; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueBuilder(self: SqliteQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + items is JOject + +
+
+ +
+
+
+
proc insertValuesBuilder(self: SqliteQuery; rows: openArray[JsonNode]): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc maxBuilder(self: SqliteQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc minBuilder(self: SqliteQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectBuilder(self: SqliteQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFindBuilder(self: SqliteQuery; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstBuilder(self: SqliteQuery): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sumBuilder(self: SqliteQuery; column: string): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateBuilder(self: SqliteQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.idx b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.idx new file mode 100644 index 0000000..f162a0f --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_builder.idx @@ -0,0 +1,15 @@ +nimTitle sqlite_builder query_builder/models/sqlite/query/sqlite_builder.html module src/allographer/query_builder/models/sqlite/query/sqlite_builder 0 +nim selectBuilder query_builder/models/sqlite/query/sqlite_builder.html#selectBuilder,SqliteQuery proc selectBuilder(self: SqliteQuery): string 8 +nim selectFirstBuilder query_builder/models/sqlite/query/sqlite_builder.html#selectFirstBuilder,SqliteQuery proc selectFirstBuilder(self: SqliteQuery): string 30 +nim selectFindBuilder query_builder/models/sqlite/query/sqlite_builder.html#selectFindBuilder,SqliteQuery,string proc selectFindBuilder(self: SqliteQuery; key: string): string 52 +nim insertValueBuilder query_builder/models/sqlite/query/sqlite_builder.html#insertValueBuilder,SqliteQuery,JsonNode proc insertValueBuilder(self: SqliteQuery; items: JsonNode): string 73 +nim insertValuesBuilder query_builder/models/sqlite/query/sqlite_builder.html#insertValuesBuilder,SqliteQuery,openArray[JsonNode] proc insertValuesBuilder(self: SqliteQuery; rows: openArray[JsonNode]): string 80 +nim updateBuilder query_builder/models/sqlite/query/sqlite_builder.html#updateBuilder,SqliteQuery,JsonNode proc updateBuilder(self: SqliteQuery; items: JsonNode): string 89 +nim deleteBuilder query_builder/models/sqlite/query/sqlite_builder.html#deleteBuilder,SqliteQuery proc deleteBuilder(self: SqliteQuery): string 102 +nim deleteByIdBuilder query_builder/models/sqlite/query/sqlite_builder.html#deleteByIdBuilder,SqliteQuery,int,string proc deleteByIdBuilder(self: SqliteQuery; id: int; key: string): string 114 +nim countBuilder query_builder/models/sqlite/query/sqlite_builder.html#countBuilder,SqliteQuery proc countBuilder(self: SqliteQuery): string 124 +nim maxBuilder query_builder/models/sqlite/query/sqlite_builder.html#maxBuilder,SqliteQuery,string proc maxBuilder(self: SqliteQuery; column: string): string 144 +nim minBuilder query_builder/models/sqlite/query/sqlite_builder.html#minBuilder,SqliteQuery,string proc minBuilder(self: SqliteQuery; column: string): string 164 +nim avgBuilder query_builder/models/sqlite/query/sqlite_builder.html#avgBuilder,SqliteQuery,string proc avgBuilder(self: SqliteQuery; column: string): string 184 +nim sumBuilder query_builder/models/sqlite/query/sqlite_builder.html#sumBuilder,SqliteQuery,string proc sumBuilder(self: SqliteQuery; column: string): string 204 +nim columnBuilder query_builder/models/sqlite/query/sqlite_builder.html#columnBuilder,SqliteQuery proc columnBuilder(self: SqliteQuery): string 224 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.html b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.html new file mode 100644 index 0000000..c71b48b --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.html @@ -0,0 +1,611 @@ + + + + + + + +src/allographer/query_builder/models/sqlite/query/sqlite_generator + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/sqlite/query/sqlite_generator

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc deleteByIdSql(self: SqliteQuery; id: int; key: string): SqliteQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteSql(self: SqliteQuery): SqliteQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBySql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc havingSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueSql(self: SqliteQuery; items: JsonNode): SqliteQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + items is JObject + +
+
+ +
+
+
+
proc insertValuesSql(self: SqliteQuery; rows: openArray[JsonNode]): SqliteQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc joinSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoinSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limitSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offsetSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBySql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhereSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectAvgSql(self: SqliteQuery; column: string): SqliteQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectByIdSql(self: SqliteQuery; key: string): SqliteQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectCountSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstSql(self: SqliteQuery): SqliteQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMaxSql(self: SqliteQuery; column: string): SqliteQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectMinSql(self: SqliteQuery; column: string): SqliteQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSumSql(self: SqliteQuery; column: string): SqliteQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateValuesSql(self: SqliteQuery; items: JsonNode): SqliteQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenStringSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereInSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenStringSql(self: SqliteQuery): SqliteQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotInSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNullSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereSql(self: SqliteQuery): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.idx b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.idx new file mode 100644 index 0000000..e44fea3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/query/sqlite_generator.idx @@ -0,0 +1,33 @@ +nimTitle sqlite_generator query_builder/models/sqlite/query/sqlite_generator.html module src/allographer/query_builder/models/sqlite/query/sqlite_generator 0 +nim selectSql query_builder/models/sqlite/query/sqlite_generator.html#selectSql,SqliteQuery proc selectSql(self: SqliteQuery): SqliteQuery 42 +nim fromSql query_builder/models/sqlite/query/sqlite_generator.html#fromSql,SqliteQuery proc fromSql(self: SqliteQuery): SqliteQuery 63 +nim selectFirstSql query_builder/models/sqlite/query/sqlite_generator.html#selectFirstSql,SqliteQuery proc selectFirstSql(self: SqliteQuery): SqliteQuery 69 +nim selectByIdSql query_builder/models/sqlite/query/sqlite_generator.html#selectByIdSql,SqliteQuery,string proc selectByIdSql(self: SqliteQuery; key: string): SqliteQuery 74 +nim joinSql query_builder/models/sqlite/query/sqlite_generator.html#joinSql,SqliteQuery proc joinSql(self: SqliteQuery): SqliteQuery 83 +nim leftJoinSql query_builder/models/sqlite/query/sqlite_generator.html#leftJoinSql,SqliteQuery proc leftJoinSql(self: SqliteQuery): SqliteQuery 95 +nim whereSql query_builder/models/sqlite/query/sqlite_generator.html#whereSql,SqliteQuery proc whereSql(self: SqliteQuery): SqliteQuery 107 +nim orWhereSql query_builder/models/sqlite/query/sqlite_generator.html#orWhereSql,SqliteQuery proc orWhereSql(self: SqliteQuery): SqliteQuery 121 +nim whereBetweenSql query_builder/models/sqlite/query/sqlite_generator.html#whereBetweenSql,SqliteQuery proc whereBetweenSql(self: SqliteQuery): SqliteQuery 135 +nim whereBetweenStringSql query_builder/models/sqlite/query/sqlite_generator.html#whereBetweenStringSql,SqliteQuery proc whereBetweenStringSql(self: SqliteQuery): SqliteQuery 149 +nim whereNotBetweenSql query_builder/models/sqlite/query/sqlite_generator.html#whereNotBetweenSql,SqliteQuery proc whereNotBetweenSql(self: SqliteQuery): SqliteQuery 163 +nim whereNotBetweenStringSql query_builder/models/sqlite/query/sqlite_generator.html#whereNotBetweenStringSql,SqliteQuery proc whereNotBetweenStringSql(self: SqliteQuery): SqliteQuery 177 +nim whereInSql query_builder/models/sqlite/query/sqlite_generator.html#whereInSql,SqliteQuery proc whereInSql(self: SqliteQuery): SqliteQuery 191 +nim whereNotInSql query_builder/models/sqlite/query/sqlite_generator.html#whereNotInSql,SqliteQuery proc whereNotInSql(self: SqliteQuery): SqliteQuery 212 +nim whereNullSql query_builder/models/sqlite/query/sqlite_generator.html#whereNullSql,SqliteQuery proc whereNullSql(self: SqliteQuery): SqliteQuery 233 +nim groupBySql query_builder/models/sqlite/query/sqlite_generator.html#groupBySql,SqliteQuery proc groupBySql(self: SqliteQuery): SqliteQuery 244 +nim havingSql query_builder/models/sqlite/query/sqlite_generator.html#havingSql,SqliteQuery proc havingSql(self: SqliteQuery): SqliteQuery 255 +nim orderBySql query_builder/models/sqlite/query/sqlite_generator.html#orderBySql,SqliteQuery proc orderBySql(self: SqliteQuery): SqliteQuery 270 +nim limitSql query_builder/models/sqlite/query/sqlite_generator.html#limitSql,SqliteQuery proc limitSql(self: SqliteQuery): SqliteQuery 283 +nim offsetSql query_builder/models/sqlite/query/sqlite_generator.html#offsetSql,SqliteQuery proc offsetSql(self: SqliteQuery): SqliteQuery 291 +nim insertSql query_builder/models/sqlite/query/sqlite_generator.html#insertSql,SqliteQuery proc insertSql(self: SqliteQuery): SqliteQuery 301 +nim insertValueSql query_builder/models/sqlite/query/sqlite_generator.html#insertValueSql,SqliteQuery,JsonNode proc insertValueSql(self: SqliteQuery; items: JsonNode): SqliteQuery 307 +nim insertValuesSql query_builder/models/sqlite/query/sqlite_generator.html#insertValuesSql,SqliteQuery,openArray[JsonNode] proc insertValuesSql(self: SqliteQuery; rows: openArray[JsonNode]): SqliteQuery 328 +nim updateSql query_builder/models/sqlite/query/sqlite_generator.html#updateSql,SqliteQuery proc updateSql(self: SqliteQuery): SqliteQuery 360 +nim updateValuesSql query_builder/models/sqlite/query/sqlite_generator.html#updateValuesSql,SqliteQuery,JsonNode proc updateValuesSql(self: SqliteQuery; items: JsonNode): SqliteQuery 370 +nim deleteSql query_builder/models/sqlite/query/sqlite_generator.html#deleteSql,SqliteQuery proc deleteSql(self: SqliteQuery): SqliteQuery 392 +nim deleteByIdSql query_builder/models/sqlite/query/sqlite_generator.html#deleteByIdSql,SqliteQuery,int,string proc deleteByIdSql(self: SqliteQuery; id: int; key: string): SqliteQuery 397 +nim selectCountSql query_builder/models/sqlite/query/sqlite_generator.html#selectCountSql,SqliteQuery proc selectCountSql(self: SqliteQuery): SqliteQuery 403 +nim selectMaxSql query_builder/models/sqlite/query/sqlite_generator.html#selectMaxSql,SqliteQuery,string proc selectMaxSql(self: SqliteQuery; column: string): SqliteQuery 414 +nim selectMinSql query_builder/models/sqlite/query/sqlite_generator.html#selectMinSql,SqliteQuery,string proc selectMinSql(self: SqliteQuery; column: string): SqliteQuery 419 +nim selectAvgSql query_builder/models/sqlite/query/sqlite_generator.html#selectAvgSql,SqliteQuery,string proc selectAvgSql(self: SqliteQuery; column: string): SqliteQuery 424 +nim selectSumSql query_builder/models/sqlite/query/sqlite_generator.html#selectSumSql,SqliteQuery,string proc selectSumSql(self: SqliteQuery; column: string): SqliteQuery 429 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.html b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.html new file mode 100644 index 0000000..0990849 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.html @@ -0,0 +1,673 @@ + + + + + + + +src/allographer/query_builder/models/sqlite/sqlite_exec + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/sqlite/sqlite_exec

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avg(self: SqliteQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc begin(self: SqliteConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columns(self: SqliteQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc commit(self: SqliteConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc count(self: SqliteQuery): Future[int] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc delete(self: SqliteQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc delete(self: SqliteQuery; id: int; key = "id"): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc exec(self: RawSqliteQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+ +
+
+
+
proc find(self: SqliteQuery; id: int; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc find(self: SqliteQuery; id: string; key = "id"): Future[Option[JsonNode]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc findPlain(self: SqliteQuery; id: int; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc findPlain(self: SqliteQuery; id: string; key = "id"): Future[seq[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc first(self: RawSqliteQuery): Future[Option[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+
+
proc first(self: SqliteQuery): Future[Option[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc firstPlain(self: RawSqliteQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+
+
proc firstPlain(self: SqliteQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc get(self: RawSqliteQuery): Future[seq[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+
+
proc get(self: SqliteQuery): Future[seq[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc getPlain(self: RawSqliteQuery): Future[seq[seq[string]]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + It is only used with raw() + +
+
+
+
proc getPlain(self: SqliteQuery): Future[seq[seq[string]]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insert(self: SqliteQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc insert(self: SqliteQuery; items: seq[JsonNode]): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc insert[T](self: SqliteQuery; items: seq[T]): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+
+
proc insert[T](self: SqliteQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc insertId(self: SqliteQuery; items: JsonNode; key = "id"): Future[string] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc insertId(self: SqliteQuery; items: seq[JsonNode]; key = "id"): Future[
+    seq[string]] {....stackTrace: false, raises: [Exception, ValueError, KeyError,
+    OSError, IOError, DbError], tags: [RootEffect, WriteDirEffect,
+                                       ReadDirEffect, ReadIOEffect,
+                                       WriteIOEffect, TimeEffect, ReadDbEffect],
+                   forbids: [].}
+
+ + + +
+
+
+
proc insertId[T](self: SqliteQuery; items: seq[T]; key = "id"): Future[
+    seq[string]] {....stackTrace: false.}
+
+ + + +
+
+
+
proc insertId[T](self: SqliteQuery; items: T; key = "id"): Future[string] {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+
+
+
proc max(self: SqliteQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc min(self: SqliteQuery; column: string): Future[Option[string]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc rollback(self: SqliteConnections): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sum(self: SqliteQuery; column: string): Future[Option[float]] {.
+    ...stackTrace: false,
+    raises: [Exception, ValueError, KeyError, OSError, IOError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc update(self: SqliteQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false,
+    raises: [Exception, KeyError, OSError, IOError, ValueError, DbError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc update[T](self: SqliteQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + + +
+
+ +
+ +
+
+
+

Templates

+
+
+
+
template seeder(rdb: SqliteConnections; tableName, column: string; body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table or specified column is empty. + +
+
+
+
template seeder(rdb: SqliteConnections; tableName: string; body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table is empty. + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.idx b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.idx new file mode 100644 index 0000000..e069a91 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_exec.idx @@ -0,0 +1,48 @@ +nimTitle sqlite_exec query_builder/models/sqlite/sqlite_exec.html module src/allographer/query_builder/models/sqlite/sqlite_exec 0 +nim get query_builder/models/sqlite/sqlite_exec.html#get,SqliteQuery proc get(self: SqliteQuery): Future[seq[JsonNode]] 517 +nim first query_builder/models/sqlite/sqlite_exec.html#first,SqliteQuery proc first(self: SqliteQuery): Future[Option[JsonNode]] 528 +nim find query_builder/models/sqlite/sqlite_exec.html#find,SqliteQuery,string,string proc find(self: SqliteQuery; id: string; key = "id"): Future[Option[JsonNode]] 539 +nim find query_builder/models/sqlite/sqlite_exec.html#find,SqliteQuery,int,string proc find(self: SqliteQuery; id: int; key = "id"): Future[Option[JsonNode]] 551 +nim getPlain query_builder/models/sqlite/sqlite_exec.html#getPlain,SqliteQuery proc getPlain(self: SqliteQuery): Future[seq[seq[string]]] 556 +nim firstPlain query_builder/models/sqlite/sqlite_exec.html#firstPlain,SqliteQuery proc firstPlain(self: SqliteQuery): Future[seq[string]] 567 +nim findPlain query_builder/models/sqlite/sqlite_exec.html#findPlain,SqliteQuery,string,string proc findPlain(self: SqliteQuery; id: string; key = "id"): Future[seq[string]] 578 +nim findPlain query_builder/models/sqlite/sqlite_exec.html#findPlain,SqliteQuery,int,string proc findPlain(self: SqliteQuery; id: int; key = "id"): Future[seq[string]] 590 +nim insert query_builder/models/sqlite/sqlite_exec.html#insert,SqliteQuery,JsonNode proc insert(self: SqliteQuery; items: JsonNode): owned(Future[void]) 644 +nim insert query_builder/models/sqlite/sqlite_exec.html#insert,SqliteQuery,seq[JsonNode] proc insert(self: SqliteQuery; items: seq[JsonNode]): owned(Future[void]) 650 +nim insertId query_builder/models/sqlite/sqlite_exec.html#insertId,SqliteQuery,JsonNode,string proc insertId(self: SqliteQuery; items: JsonNode; key = "id"): Future[string] 656 +nim insertId query_builder/models/sqlite/sqlite_exec.html#insertId,SqliteQuery,seq[JsonNode],string proc insertId(self: SqliteQuery; items: seq[JsonNode]; key = "id"): Future[\n seq[string]] 662 +nim insert query_builder/models/sqlite/sqlite_exec.html#insert,SqliteQuery,T proc insert[T](self: SqliteQuery; items: T): owned(Future[void]) 672 +nim insert query_builder/models/sqlite/sqlite_exec.html#insert,SqliteQuery,seq[T] proc insert[T](self: SqliteQuery; items: seq[T]): owned(Future[void]) 678 +nim insertId query_builder/models/sqlite/sqlite_exec.html#insertId,SqliteQuery,T,string proc insertId[T](self: SqliteQuery; items: T; key = "id"): Future[string] 685 +nim insertId query_builder/models/sqlite/sqlite_exec.html#insertId,SqliteQuery,seq[T],string proc insertId[T](self: SqliteQuery; items: seq[T]; key = "id"): Future[seq[string]] 691 +nim update query_builder/models/sqlite/sqlite_exec.html#update,SqliteQuery,JsonNode proc update(self: SqliteQuery; items: JsonNode): owned(Future[void]) 701 +nim update query_builder/models/sqlite/sqlite_exec.html#update,SqliteQuery,T proc update[T](self: SqliteQuery; items: T): owned(Future[void]) 707 +nim delete query_builder/models/sqlite/sqlite_exec.html#delete,SqliteQuery proc delete(self: SqliteQuery): owned(Future[void]) 713 +nim delete query_builder/models/sqlite/sqlite_exec.html#delete,SqliteQuery,int,string proc delete(self: SqliteQuery; id: int; key = "id"): owned(Future[void]) 719 +nim columns query_builder/models/sqlite/sqlite_exec.html#columns,SqliteQuery proc columns(self: SqliteQuery): Future[seq[string]] 726 +nim count query_builder/models/sqlite/sqlite_exec.html#count,SqliteQuery proc count(self: SqliteQuery): Future[int] 732 +nim min query_builder/models/sqlite/sqlite_exec.html#min,SqliteQuery,string proc min(self: SqliteQuery; column: string): Future[Option[string]] 742 +nim max query_builder/models/sqlite/sqlite_exec.html#max,SqliteQuery,string proc max(self: SqliteQuery; column: string): Future[Option[string]] 758 +nim avg query_builder/models/sqlite/sqlite_exec.html#avg,SqliteQuery,string proc avg(self: SqliteQuery; column: string): Future[Option[float]] 774 +nim sum query_builder/models/sqlite/sqlite_exec.html#sum,SqliteQuery,string proc sum(self: SqliteQuery; column: string): Future[Option[float]] 784 +nim begin query_builder/models/sqlite/sqlite_exec.html#begin,SqliteConnections proc begin(self: SqliteConnections): owned(Future[void]) 794 +nim rollback query_builder/models/sqlite/sqlite_exec.html#rollback,SqliteConnections proc rollback(self: SqliteConnections): owned(Future[void]) 799 +nim commit query_builder/models/sqlite/sqlite_exec.html#commit,SqliteConnections proc commit(self: SqliteConnections): owned(Future[void]) 804 +nim get query_builder/models/sqlite/sqlite_exec.html#get,RawSqliteQuery proc get(self: RawSqliteQuery): Future[seq[JsonNode]] 809 +nim getPlain query_builder/models/sqlite/sqlite_exec.html#getPlain,RawSqliteQuery proc getPlain(self: RawSqliteQuery): Future[seq[seq[string]]] 815 +nim exec query_builder/models/sqlite/sqlite_exec.html#exec,RawSqliteQuery proc exec(self: RawSqliteQuery): owned(Future[void]) 821 +nim first query_builder/models/sqlite/sqlite_exec.html#first,RawSqliteQuery proc first(self: RawSqliteQuery): Future[Option[JsonNode]] 827 +nim firstPlain query_builder/models/sqlite/sqlite_exec.html#firstPlain,RawSqliteQuery proc firstPlain(self: RawSqliteQuery): Future[seq[string]] 833 +nim seeder query_builder/models/sqlite/sqlite_exec.html#seeder.t,SqliteConnections,string,untyped template seeder(rdb: SqliteConnections; tableName: string; body: untyped): untyped 839 +nim seeder query_builder/models/sqlite/sqlite_exec.html#seeder.t,SqliteConnections,string,string,untyped template seeder(rdb: SqliteConnections; tableName, column: string; body: untyped): untyped 846 +nimgrp getplain query_builder/models/sqlite/sqlite_exec.html#getPlain-procs-all proc 556 +nimgrp firstplain query_builder/models/sqlite/sqlite_exec.html#firstPlain-procs-all proc 567 +nimgrp findplain query_builder/models/sqlite/sqlite_exec.html#findPlain-procs-all proc 578 +nimgrp insertid query_builder/models/sqlite/sqlite_exec.html#insertId-procs-all proc 656 +nimgrp delete query_builder/models/sqlite/sqlite_exec.html#delete-procs-all proc 713 +nimgrp get query_builder/models/sqlite/sqlite_exec.html#get-procs-all proc 517 +nimgrp update query_builder/models/sqlite/sqlite_exec.html#update-procs-all proc 701 +nimgrp find query_builder/models/sqlite/sqlite_exec.html#find-procs-all proc 539 +nimgrp first query_builder/models/sqlite/sqlite_exec.html#first-procs-all proc 528 +nimgrp insert query_builder/models/sqlite/sqlite_exec.html#insert-procs-all proc 644 +nimgrp seeder query_builder/models/sqlite/sqlite_exec.html#seeder-templates-all template 839 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.html b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.html new file mode 100644 index 0000000..82d1604 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.html @@ -0,0 +1,135 @@ + + + + + + + +src/allographer/query_builder/models/sqlite/sqlite_open + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/sqlite/sqlite_open

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dbOpen(_: type SQLite3; database: string = ""; maxConnections: int = 1;
+            timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;
+            logDir = ""): SqliteConnections
+
+ + + +
+
+
+
proc dbOpen(_: type SQLite3; databaseUrl: DatabaseUrl; maxConnections: int = 1;
+            timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;
+            logDir = ""): SqliteConnections
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.idx b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.idx new file mode 100644 index 0000000..b65cc45 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_open.idx @@ -0,0 +1,4 @@ +nimTitle sqlite_open query_builder/models/sqlite/sqlite_open.html module src/allographer/query_builder/models/sqlite/sqlite_open 0 +nim dbOpen query_builder/models/sqlite/sqlite_open.html#dbOpen,typeSQLite3,string,int,int,string proc dbOpen(_: type SQLite3; database: string = ""; maxConnections: int = 1;\n timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;\n logDir = ""): SqliteConnections 35 +nim dbOpen query_builder/models/sqlite/sqlite_open.html#dbOpen,typeSQLite3,DatabaseUrl,int,int,string proc dbOpen(_: type SQLite3; databaseUrl: DatabaseUrl; maxConnections: int = 1;\n timeout = 30; shouldDisplayLog = false; shouldOutputLogFile = false;\n logDir = ""): SqliteConnections 46 +nimgrp dbopen query_builder/models/sqlite/sqlite_open.html#dbOpen-procs-all proc 35 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.html b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.html new file mode 100644 index 0000000..429fd2f --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.html @@ -0,0 +1,470 @@ + + + + + + + +src/allographer/query_builder/models/sqlite/sqlite_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/sqlite/sqlite_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc `distinct`(self: SqliteQuery): SqliteQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBy(self: SqliteQuery; column: string): SqliteQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc having(self: SqliteQuery; column: string; symbol: string;
+            value: string | int | float | bool): SqliteQuery
+
+ + + +
+
+
+
proc having(self: SqliteQuery; column: string; symbol: string; value: nil.type): SqliteQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc join(self: SqliteQuery; table: string; column1: string; symbol: string;
+          column2: string): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc leftJoin(self: SqliteQuery; table: string; column1: string; symbol: string;
+              column2: string): SqliteQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limit(self: SqliteQuery; num: int): SqliteQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc offset(self: SqliteQuery; num: int): SqliteQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBy(self: SqliteQuery; column: string; order: Order): SqliteQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhere(self: SqliteQuery; column: string; symbol: string;
+             value: string | int | float | bool): SqliteQuery
+
+ + + +
+
+
+
proc orWhere(self: SqliteQuery; column: string; symbol: string; value: nil.type): SqliteQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc raw(self: SqliteConnections; sql: string; arges = newJArray()): RawSqliteQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ +

arges is JArray

+

can't use BLOB data.

+ + +
+
+ +
+
+
+
proc select(self: SqliteConnections; columnsArg: varargs[string]): SqliteQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc table(self: SqliteConnections; tableArg: string): SqliteQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+
+
proc table(self: SqliteQuery; tableArg: string): SqliteQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc where(self: SqliteQuery; column: string; symbol: string;
+           value: string | int | float): SqliteQuery
+
+ + + +
+
+
+
proc where(self: SqliteQuery; column: string; symbol: string; value: bool): SqliteQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc where(self: SqliteQuery; column: string; symbol: string; value: nil.type): SqliteQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetween(self: SqliteQuery; column: string;
+                  width: array[2, int | float]): SqliteQuery
+
+ + + +
+
+
+
proc whereBetween(self: SqliteQuery; column: string; width: array[2, string]): SqliteQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereIn(self: SqliteQuery; column: string; width: seq[int | float | string]): SqliteQuery
+
+ + + +
+
+ +
+
+
+
proc whereNotBetween(self: SqliteQuery; column: string;
+                     width: array[2, int | float]): SqliteQuery
+
+ + + +
+
+
+
proc whereNotBetween(self: SqliteQuery; column: string; width: array[2, string]): SqliteQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotIn(self: SqliteQuery; column: string;
+                width: seq[int | float | string]): SqliteQuery
+
+ + + +
+
+ +
+
+
+
proc whereNull(self: SqliteQuery; column: string): SqliteQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.idx b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.idx new file mode 100644 index 0000000..febda3e --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_query.idx @@ -0,0 +1,32 @@ +nimTitle sqlite_query query_builder/models/sqlite/sqlite_query.html module src/allographer/query_builder/models/sqlite/sqlite_query 0 +nim select query_builder/models/sqlite/sqlite_query.html#select,SqliteConnections,varargs[string] proc select(self: SqliteConnections; columnsArg: varargs[string]): SqliteQuery 11 +nim table query_builder/models/sqlite/sqlite_query.html#table,SqliteConnections,string proc table(self: SqliteConnections; tableArg: string): SqliteQuery 31 +nim table query_builder/models/sqlite/sqlite_query.html#table,SqliteQuery,string proc table(self: SqliteQuery; tableArg: string): SqliteQuery 47 +nim `distinct` query_builder/models/sqlite/sqlite_query.html#distinct,SqliteQuery proc `distinct`(self: SqliteQuery): SqliteQuery 52 +nim join query_builder/models/sqlite/sqlite_query.html#join,SqliteQuery,string,string,string,string proc join(self: SqliteQuery; table: string; column1: string; symbol: string;\n column2: string): SqliteQuery 58 +nim leftJoin query_builder/models/sqlite/sqlite_query.html#leftJoin,SqliteQuery,string,string,string,string proc leftJoin(self: SqliteQuery; table: string; column1: string; symbol: string;\n column2: string): SqliteQuery 77 +nim where query_builder/models/sqlite/sqlite_query.html#where,SqliteQuery,string,string, proc where(self: SqliteQuery; column: string; symbol: string;\n value: string | int | float): SqliteQuery 99 +nim where query_builder/models/sqlite/sqlite_query.html#where,SqliteQuery,string,string,bool proc where(self: SqliteQuery; column: string; symbol: string; value: bool): SqliteQuery 126 +nim where query_builder/models/sqlite/sqlite_query.html#where,SqliteQuery,string,string,_2 proc where(self: SqliteQuery; column: string; symbol: string; value: nil.type): SqliteQuery 153 +nim orWhere query_builder/models/sqlite/sqlite_query.html#orWhere,SqliteQuery,string,string, proc orWhere(self: SqliteQuery; column: string; symbol: string;\n value: string | int | float | bool): SqliteQuery 177 +nim orWhere query_builder/models/sqlite/sqlite_query.html#orWhere,SqliteQuery,string,string,_2 proc orWhere(self: SqliteQuery; column: string; symbol: string; value: nil.type): SqliteQuery 203 +nim whereBetween query_builder/models/sqlite/sqlite_query.html#whereBetween,SqliteQuery,string,array[,] proc whereBetween(self: SqliteQuery; column: string; width: array[2, int | float]): SqliteQuery 224 +nim whereBetween query_builder/models/sqlite/sqlite_query.html#whereBetween,SqliteQuery,string,array[,string] proc whereBetween(self: SqliteQuery; column: string; width: array[2, string]): SqliteQuery 237 +nim whereNotBetween query_builder/models/sqlite/sqlite_query.html#whereNotBetween,SqliteQuery,string,array[,] proc whereNotBetween(self: SqliteQuery; column: string; width: array[2, int | float]): SqliteQuery 250 +nim whereNotBetween query_builder/models/sqlite/sqlite_query.html#whereNotBetween,SqliteQuery,string,array[,string] proc whereNotBetween(self: SqliteQuery; column: string; width: array[2, string]): SqliteQuery 263 +nim whereIn query_builder/models/sqlite/sqlite_query.html#whereIn,SqliteQuery,string,seq[] proc whereIn(self: SqliteQuery; column: string; width: seq[int | float | string]): SqliteQuery 276 +nim whereNotIn query_builder/models/sqlite/sqlite_query.html#whereNotIn,SqliteQuery,string,seq[] proc whereNotIn(self: SqliteQuery; column: string; width: seq[int | float | string]): SqliteQuery 290 +nim whereNull query_builder/models/sqlite/sqlite_query.html#whereNull,SqliteQuery,string proc whereNull(self: SqliteQuery; column: string): SqliteQuery 304 +nim groupBy query_builder/models/sqlite/sqlite_query.html#groupBy,SqliteQuery,string proc groupBy(self: SqliteQuery; column: string): SqliteQuery 316 +nim having query_builder/models/sqlite/sqlite_query.html#having,SqliteQuery,string,string, proc having(self: SqliteQuery; column: string; symbol: string;\n value: string | int | float | bool): SqliteQuery 324 +nim having query_builder/models/sqlite/sqlite_query.html#having,SqliteQuery,string,string,_2 proc having(self: SqliteQuery; column: string; symbol: string; value: nil.type): SqliteQuery 350 +nim orderBy query_builder/models/sqlite/sqlite_query.html#orderBy,SqliteQuery,string,Order proc orderBy(self: SqliteQuery; column: string; order: Order): SqliteQuery 374 +nim limit query_builder/models/sqlite/sqlite_query.html#limit,SqliteQuery,int proc limit(self: SqliteQuery; num: int): SqliteQuery 388 +nim offset query_builder/models/sqlite/sqlite_query.html#offset,SqliteQuery,int proc offset(self: SqliteQuery; num: int): SqliteQuery 393 +nim raw query_builder/models/sqlite/sqlite_query.html#raw,SqliteConnections,string proc raw(self: SqliteConnections; sql: string; arges = newJArray()): RawSqliteQuery 398 +nimgrp where query_builder/models/sqlite/sqlite_query.html#where-procs-all proc 99 +nimgrp wherebetween query_builder/models/sqlite/sqlite_query.html#whereBetween-procs-all proc 224 +nimgrp wherenotbetween query_builder/models/sqlite/sqlite_query.html#whereNotBetween-procs-all proc 250 +nimgrp orwhere query_builder/models/sqlite/sqlite_query.html#orWhere-procs-all proc 177 +nimgrp having query_builder/models/sqlite/sqlite_query.html#having-procs-all proc 324 +nimgrp table query_builder/models/sqlite/sqlite_query.html#table-procs-all proc 31 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.html b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.html new file mode 100644 index 0000000..8d49ef2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.html @@ -0,0 +1,114 @@ + + + + + + + +src/allographer/query_builder/models/sqlite/sqlite_transaction + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/sqlite/sqlite_transaction

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Templates

+
+
+
+
template transaction(rdb: SqliteConnections; callback: untyped)
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.idx b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.idx new file mode 100644 index 0000000..a23941a --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_transaction.idx @@ -0,0 +1,2 @@ +nimTitle sqlite_transaction query_builder/models/sqlite/sqlite_transaction.html module src/allographer/query_builder/models/sqlite/sqlite_transaction 0 +nim transaction query_builder/models/sqlite/sqlite_transaction.html#transaction.t,SqliteConnections,untyped template transaction(rdb: SqliteConnections; callback: untyped) 41 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.html b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.html new file mode 100644 index 0000000..d96cd69 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.html @@ -0,0 +1,232 @@ + + + + + + + +src/allographer/query_builder/models/sqlite/sqlite_types + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/sqlite/sqlite_types

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

created by let rdb = dbOpen(SQLite3, "/path/to/sqlite.db")created by rdb.select("columnName") or rdb.table("tableName")

+ +
+

Types

+
+
+
Connection = ref object
+  conn*: PSqlite3
+  isBusy*: bool
+  createdAt*: int64
+
+ + + +
+
+
+
Connections = ref object
+  conns*: seq[Connection]
+  timeout*: int              ## `getFreeConn` が接続を待つときに積む Future。`returnConn` が先頭から 1 件だけ完了させる。
+  waiters*: Deque[Future[void]] ## `exec` / `insertId` 用。テーブルごとに PRAGMA table_info の結果を初回のみ保持する。
+  columnTypeCache*: Table[string, seq[(string, string)]]
+
+ + + +
+
+
+
RawSqliteQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
SQLite3 = object
+
+ + + +
+
+
+
SqliteConnections = ref object
+  log*: LogSetting
+  pools*: Connections
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+
+
SqliteQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+  isInTransaction*: bool
+  transactionConn*: int
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc `$`(self: SqliteConnections | SqliteQuery | RawSqliteQuery): string
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.idx b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.idx new file mode 100644 index 0000000..ed6143e --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/sqlite/sqlite_types.idx @@ -0,0 +1,8 @@ +nimTitle sqlite_types query_builder/models/sqlite/sqlite_types.html module src/allographer/query_builder/models/sqlite/sqlite_types 0 +nim SQLite3 query_builder/models/sqlite/sqlite_types.html#SQLite3 object SQLite3 9 +nim Connection query_builder/models/sqlite/sqlite_types.html#Connection type Connection 12 +nim Connections query_builder/models/sqlite/sqlite_types.html#Connections type Connections 18 +nim SqliteConnections query_builder/models/sqlite/sqlite_types.html#SqliteConnections type SqliteConnections 28 +nim SqliteQuery query_builder/models/sqlite/sqlite_types.html#SqliteQuery type SqliteQuery 37 +nim RawSqliteQuery query_builder/models/sqlite/sqlite_types.html#RawSqliteQuery type RawSqliteQuery 48 +nim `$` query_builder/models/sqlite/sqlite_types.html#$ proc `$`(self: SqliteConnections | SqliteQuery | RawSqliteQuery): string 59 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.html b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.html new file mode 100644 index 0000000..57296d9 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.html @@ -0,0 +1,291 @@ + + + + + + + +src/allographer/query_builder/models/surreal/query/surreal_builder + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/surreal/query/surreal_builder

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc countBuilder(self: SurrealQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteBuilder(self: SurrealQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteByIdBuilder(self: SurrealQuery; id: string): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueBuilder(self: SurrealQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesBuilder(self: SurrealQuery; items: openArray[JsonNode]): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectAvgBuilder(self: SurrealQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectBuilder(self: SurrealQuery): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFindBuilder(self: SurrealQuery; id: SurrealId; key: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstBuilder(self: SurrealQuery): string {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSumBuilder(self: SurrealQuery; column: string): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateBuilder(self: SurrealQuery; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateMergeBuilder(self: SurrealQuery; id: string; items: JsonNode): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.idx b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.idx new file mode 100644 index 0000000..bc489a5 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_builder.idx @@ -0,0 +1,13 @@ +nimTitle surreal_builder query_builder/models/surreal/query/surreal_builder.html module src/allographer/query_builder/models/surreal/query/surreal_builder 0 +nim selectBuilder query_builder/models/surreal/query/surreal_builder.html#selectBuilder,SurrealQuery proc selectBuilder(self: SurrealQuery): string 8 +nim selectFirstBuilder query_builder/models/surreal/query/surreal_builder.html#selectFirstBuilder,SurrealQuery proc selectFirstBuilder(self: SurrealQuery): string 31 +nim selectFindBuilder query_builder/models/surreal/query/surreal_builder.html#selectFindBuilder,SurrealQuery,SurrealId,string proc selectFindBuilder(self: SurrealQuery; id: SurrealId; key: string): string 54 +nim insertValueBuilder query_builder/models/surreal/query/surreal_builder.html#insertValueBuilder,SurrealQuery,JsonNode proc insertValueBuilder(self: SurrealQuery; items: JsonNode): string 75 +nim insertValuesBuilder query_builder/models/surreal/query/surreal_builder.html#insertValuesBuilder,SurrealQuery,openArray[JsonNode] proc insertValuesBuilder(self: SurrealQuery; items: openArray[JsonNode]): string 82 +nim updateBuilder query_builder/models/surreal/query/surreal_builder.html#updateBuilder,SurrealQuery,JsonNode proc updateBuilder(self: SurrealQuery; items: JsonNode): string 92 +nim updateMergeBuilder query_builder/models/surreal/query/surreal_builder.html#updateMergeBuilder,SurrealQuery,string,JsonNode proc updateMergeBuilder(self: SurrealQuery; id: string; items: JsonNode): string 104 +nim deleteBuilder query_builder/models/surreal/query/surreal_builder.html#deleteBuilder,SurrealQuery proc deleteBuilder(self: SurrealQuery): string 117 +nim deleteByIdBuilder query_builder/models/surreal/query/surreal_builder.html#deleteByIdBuilder,SurrealQuery,string proc deleteByIdBuilder(self: SurrealQuery; id: string): string 127 +nim countBuilder query_builder/models/surreal/query/surreal_builder.html#countBuilder,SurrealQuery proc countBuilder(self: SurrealQuery): string 136 +nim selectAvgBuilder query_builder/models/surreal/query/surreal_builder.html#selectAvgBuilder,SurrealQuery,string proc selectAvgBuilder(self: SurrealQuery; column: string): string 157 +nim selectSumBuilder query_builder/models/surreal/query/surreal_builder.html#selectSumBuilder,SurrealQuery,string proc selectSumBuilder(self: SurrealQuery; column: string): string 178 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.html b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.html new file mode 100644 index 0000000..dea4e32 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.html @@ -0,0 +1,597 @@ + + + + + + + +src/allographer/query_builder/models/surreal/query/surreal_generator + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/surreal/query/surreal_generator

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc deleteByIdSql(self: SurrealQuery; id: string): SurrealQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc deleteSql(self: SurrealQuery): SurrealQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetchSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fromSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupAllSql(self: SurrealQuery): SurrealQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBySql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValueSql(self: SurrealQuery; items: JsonNode): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc insertValuesSql(self: SurrealQuery; rows: openArray[JsonNode]): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limitSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc maxFromSql(self: SurrealQuery; column: string): SurrealQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBySql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhereSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc parallelSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc quote(input: string): string {....raises: [], tags: [], forbids: [].}
+
+ +

"User.id as userId" => User.id as userId

+

"count(id) as count" => count(id) as count

+

"math::max(id) as maxId" => math::max(id) as maxId

+ + +
+
+ +
+
+
+
proc selectAvgSql(self: SurrealQuery; column: string): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectByIdSql(self: SurrealQuery; id: SurrealId; key: string): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectCountSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectFirstSql(self: SurrealQuery): SurrealQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc selectSumSql(self: SurrealQuery; column: string): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc startSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateMergeSql(self: SurrealQuery; id: string; items: JsonNode): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc updateValuesSql(self: SurrealQuery; items: JsonNode): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetweenSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereInSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotBetweenSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNotInSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereNullSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereSql(self: SurrealQuery): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.idx b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.idx new file mode 100644 index 0000000..ce550db --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/query/surreal_generator.idx @@ -0,0 +1,32 @@ +nimTitle surreal_generator query_builder/models/surreal/query/surreal_generator.html module src/allographer/query_builder/models/surreal/query/surreal_generator 0 +nim quote query_builder/models/surreal/query/surreal_generator.html#quote,string proc quote(input: string): string 11 +nim selectSql query_builder/models/surreal/query/surreal_generator.html#selectSql,SurrealQuery proc selectSql(self: SurrealQuery): SurrealQuery 49 +nim fromSql query_builder/models/surreal/query/surreal_generator.html#fromSql,SurrealQuery proc fromSql(self: SurrealQuery): SurrealQuery 65 +nim maxFromSql query_builder/models/surreal/query/surreal_generator.html#maxFromSql,SurrealQuery,string proc maxFromSql(self: SurrealQuery; column: string): SurrealQuery 72 +nim selectFirstSql query_builder/models/surreal/query/surreal_generator.html#selectFirstSql,SurrealQuery proc selectFirstSql(self: SurrealQuery): SurrealQuery 79 +nim selectByIdSql query_builder/models/surreal/query/surreal_generator.html#selectByIdSql,SurrealQuery,SurrealId,string proc selectByIdSql(self: SurrealQuery; id: SurrealId; key: string): SurrealQuery 84 +nim whereSql query_builder/models/surreal/query/surreal_generator.html#whereSql,SurrealQuery proc whereSql(self: SurrealQuery): SurrealQuery 96 +nim orWhereSql query_builder/models/surreal/query/surreal_generator.html#orWhereSql,SurrealQuery proc orWhereSql(self: SurrealQuery): SurrealQuery 113 +nim whereBetweenSql query_builder/models/surreal/query/surreal_generator.html#whereBetweenSql,SurrealQuery proc whereBetweenSql(self: SurrealQuery): SurrealQuery 130 +nim whereNotBetweenSql query_builder/models/surreal/query/surreal_generator.html#whereNotBetweenSql,SurrealQuery proc whereNotBetweenSql(self: SurrealQuery): SurrealQuery 147 +nim whereInSql query_builder/models/surreal/query/surreal_generator.html#whereInSql,SurrealQuery proc whereInSql(self: SurrealQuery): SurrealQuery 164 +nim whereNotInSql query_builder/models/surreal/query/surreal_generator.html#whereNotInSql,SurrealQuery proc whereNotInSql(self: SurrealQuery): SurrealQuery 187 +nim whereNullSql query_builder/models/surreal/query/surreal_generator.html#whereNullSql,SurrealQuery proc whereNullSql(self: SurrealQuery): SurrealQuery 210 +nim groupBySql query_builder/models/surreal/query/surreal_generator.html#groupBySql,SurrealQuery proc groupBySql(self: SurrealQuery): SurrealQuery 224 +nim orderBySql query_builder/models/surreal/query/surreal_generator.html#orderBySql,SurrealQuery proc orderBySql(self: SurrealQuery): SurrealQuery 236 +nim limitSql query_builder/models/surreal/query/surreal_generator.html#limitSql,SurrealQuery proc limitSql(self: SurrealQuery): SurrealQuery 251 +nim startSql query_builder/models/surreal/query/surreal_generator.html#startSql,SurrealQuery proc startSql(self: SurrealQuery): SurrealQuery 258 +nim fetchSql query_builder/models/surreal/query/surreal_generator.html#fetchSql,SurrealQuery proc fetchSql(self: SurrealQuery): SurrealQuery 265 +nim parallelSql query_builder/models/surreal/query/surreal_generator.html#parallelSql,SurrealQuery proc parallelSql(self: SurrealQuery): SurrealQuery 276 +nim insertSql query_builder/models/surreal/query/surreal_generator.html#insertSql,SurrealQuery proc insertSql(self: SurrealQuery): SurrealQuery 284 +nim insertValueSql query_builder/models/surreal/query/surreal_generator.html#insertValueSql,SurrealQuery,JsonNode proc insertValueSql(self: SurrealQuery; items: JsonNode): SurrealQuery 291 +nim insertValuesSql query_builder/models/surreal/query/surreal_generator.html#insertValuesSql,SurrealQuery,openArray[JsonNode] proc insertValuesSql(self: SurrealQuery; rows: openArray[JsonNode]): SurrealQuery 303 +nim updateSql query_builder/models/surreal/query/surreal_generator.html#updateSql,SurrealQuery proc updateSql(self: SurrealQuery): SurrealQuery 321 +nim updateValuesSql query_builder/models/surreal/query/surreal_generator.html#updateValuesSql,SurrealQuery,JsonNode proc updateValuesSql(self: SurrealQuery; items: JsonNode): SurrealQuery 332 +nim updateMergeSql query_builder/models/surreal/query/surreal_generator.html#updateMergeSql,SurrealQuery,string,JsonNode proc updateMergeSql(self: SurrealQuery; id: string; items: JsonNode): SurrealQuery 343 +nim deleteSql query_builder/models/surreal/query/surreal_generator.html#deleteSql,SurrealQuery proc deleteSql(self: SurrealQuery): SurrealQuery 350 +nim deleteByIdSql query_builder/models/surreal/query/surreal_generator.html#deleteByIdSql,SurrealQuery,string proc deleteByIdSql(self: SurrealQuery; id: string): SurrealQuery 355 +nim selectCountSql query_builder/models/surreal/query/surreal_generator.html#selectCountSql,SurrealQuery proc selectCountSql(self: SurrealQuery): SurrealQuery 363 +nim selectAvgSql query_builder/models/surreal/query/surreal_generator.html#selectAvgSql,SurrealQuery,string proc selectAvgSql(self: SurrealQuery; column: string): SurrealQuery 375 +nim selectSumSql query_builder/models/surreal/query/surreal_generator.html#selectSumSql,SurrealQuery,string proc selectSumSql(self: SurrealQuery; column: string): SurrealQuery 382 +nim groupAllSql query_builder/models/surreal/query/surreal_generator.html#groupAllSql,SurrealQuery proc groupAllSql(self: SurrealQuery): SurrealQuery 389 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.html b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.html new file mode 100644 index 0000000..d5e0124 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.html @@ -0,0 +1,579 @@ + + + + + + + +src/allographer/query_builder/models/surreal/surreal_exec + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/surreal/surreal_exec

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc avg(self: SurrealQuery; column: string): Future[float] {....stackTrace: false, raises: [
+    Exception, ValueError, KeyError, OSError, IOError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc columns(self: SurrealQuery): Future[seq[string]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc count(self: SurrealQuery): Future[int] {....stackTrace: false, raises: [
+    Exception, ValueError, KeyError, OSError, IOError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc delete(self: SurrealQuery): owned(Future[void]) {....stackTrace: false, raises: [
+    Exception, KeyError, OSError, IOError, ValueError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/delete + +
+
+
+
proc delete(self: SurrealQuery; id: SurrealId): owned(Future[void]) {.
+    ...stackTrace: false, raises: [Exception, OSError, IOError, ValueError,
+                                DbError, HttpRequestError, LibraryError,
+                                SslError, ProtocolError, KeyError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/delete + +
+
+ +
+
+
+
proc exec(self: RawSurrealQuery): owned(Future[void]) {....stackTrace: false,
+    raises: [Exception, IOError, OSError], tags: [RootEffect, WriteDirEffect,
+    ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect], forbids: [].}
+
+ +

It is only used with raw()

+

https://surrealdb.com/docs/integration/http#sql

+

https://surrealdb.com/docs/surrealql

+ + +
+
+ +
+
+
+
proc find(self: SurrealQuery; id: SurrealId; key = "id"): Future[
+    Option[JsonNode]] {....stackTrace: false, raises: [Exception, ValueError,
+    KeyError, IOError, OSError], tags: [RootEffect, WriteDirEffect,
+                                        ReadDirEffect, ReadIOEffect,
+                                        WriteIOEffect, TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc first(self: RawSurrealQuery): Future[Option[JsonNode]] {....stackTrace: false, raises: [
+    Exception, ValueError, OSError, IOError, DbError, HttpRequestError,
+    LibraryError, SslError, ProtocolError, KeyError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ + It is only used with raw() + +
+
+
+
proc first(self: SurrealQuery): Future[Option[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc get(self: RawSurrealQuery): Future[seq[JsonNode]] {....stackTrace: false, raises: [
+    Exception, ValueError, OSError, IOError, DbError, HttpRequestError,
+    LibraryError, SslError, ProtocolError, KeyError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ + It is only used with raw() + +
+
+
+
proc get(self: SurrealQuery): Future[seq[JsonNode]] {....stackTrace: false,
+    raises: [Exception, ValueError, KeyError, IOError, OSError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/select + +
+
+ +
+
+
+
proc info(self: RawSurrealQuery): Future[JsonNode] {....stackTrace: false,
+    raises: [Exception, ValueError, IOError, OSError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ +

Get all response.

+

https://surrealdb.com/docs/integration/http#sql

+

https://surrealdb.com/docs/surrealql

+ + +
+
+ +
+
+
+
proc insert(self: SurrealQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false, raises: [Exception, KeyError, OSError, IOError,
+                                ValueError, DbError, HttpRequestError,
+                                LibraryError, SslError, ProtocolError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/insert + +
+
+
+
proc insert(self: SurrealQuery; items: seq[JsonNode]): owned(Future[void]) {.
+    ...stackTrace: false, raises: [Exception, KeyError, OSError, IOError,
+                                ValueError, DbError, HttpRequestError,
+                                LibraryError, SslError, ProtocolError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/insert + +
+
+
+
proc insert[T](self: SurrealQuery; items: seq[T]): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + https://surrealdb.com/docs/surrealql/statements/insert + +
+
+
+
proc insert[T](self: SurrealQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + https://surrealdb.com/docs/surrealql/statements/insert + +
+
+ +
+
+
+
proc insertId(self: SurrealQuery; items: JsonNode; key = "id"): Future[SurrealId] {.
+    ...stackTrace: false, raises: [Exception, ValueError, KeyError, OSError,
+                                IOError, DbError, HttpRequestError,
+                                LibraryError, SslError, ProtocolError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/insert + +
+
+
+
proc insertId(self: SurrealQuery; items: seq[JsonNode]; key = "id"): Future[
+    seq[SurrealId]] {....stackTrace: false, raises: [Exception, ValueError,
+    KeyError, OSError, IOError, DbError, HttpRequestError, LibraryError,
+    SslError, ProtocolError], tags: [RootEffect, WriteDirEffect, ReadDirEffect,
+                                     ReadIOEffect, WriteIOEffect, TimeEffect],
+                      forbids: [].}
+
+ + + +
+
+
+
proc insertId[T](self: SurrealQuery; items: seq[T]; key = "id"): Future[
+    seq[SurrealId]] {....stackTrace: false.}
+
+ + + +
+
+
+
proc insertId[T](self: SurrealQuery; items: T; key = "id"): Future[SurrealId] {.
+    ...stackTrace: false.}
+
+ + https://surrealdb.com/docs/surrealql/statements/insert + +
+
+ +
+
+
+
proc max(self: SurrealQuery; column: string; collaction: Collation = None): Future[
+    string] {....stackTrace: false, raises: [Exception, ValueError, KeyError,
+    OSError, IOError, DbError, HttpRequestError, LibraryError, SslError,
+    ProtocolError], tags: [RootEffect, WriteDirEffect, ReadDirEffect,
+                           ReadIOEffect, WriteIOEffect, TimeEffect], forbids: [].}
+
+ + = ORDER BY {column} {collaction} DESC LIMIT 1 + +
+
+ +
+
+
+
proc min(self: SurrealQuery; column: string; collaction: Collation = None): Future[
+    string] {....stackTrace: false, raises: [Exception, ValueError, KeyError,
+    OSError, IOError, DbError, HttpRequestError, LibraryError, SslError,
+    ProtocolError], tags: [RootEffect, WriteDirEffect, ReadDirEffect,
+                           ReadIOEffect, WriteIOEffect, TimeEffect], forbids: [].}
+
+ + = ORDER BY {column} {collaction} ASC LIMIT 1 + +
+
+ +
+
+
+
proc sum(self: SurrealQuery; column: string): Future[float] {....stackTrace: false, raises: [
+    Exception, ValueError, KeyError, OSError, IOError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc update(self: SurrealConnections; id: SurrealId; items: JsonNode): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception, KeyError, OSError,
+    IOError, ValueError, DbError, HttpRequestError, LibraryError, SslError,
+    ProtocolError], tags: [RootEffect, WriteDirEffect, ReadDirEffect,
+                           ReadIOEffect, WriteIOEffect, TimeEffect], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/update + +
+
+
+
proc update(self: SurrealQuery; items: JsonNode): owned(Future[void]) {.
+    ...stackTrace: false, raises: [Exception, KeyError, OSError, IOError,
+                                ValueError, DbError, HttpRequestError,
+                                LibraryError, SslError, ProtocolError], tags: [
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    TimeEffect], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/statements/update + +
+
+
+
proc update[T](self: SurrealConnections; id: SurrealId; items: T): owned(
+    Future[void]) {....stackTrace: false.}
+
+ + https://surrealdb.com/docs/surrealql/statements/update + +
+
+
+
proc update[T](self: SurrealQuery; items: T): owned(Future[void]) {.
+    ...stackTrace: false.}
+
+ + https://surrealdb.com/docs/surrealql/statements/update + +
+
+ +
+ +
+
+
+

Templates

+
+
+
+
template seeder(rdb: SurrealConnections; tableName, column: string;
+                body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table or specified column is empty. + +
+
+
+
template seeder(rdb: SurrealConnections; tableName: string; body: untyped): untyped
+
+ + The seeder block allows the code in the block to work only when the table is empty. + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.idx b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.idx new file mode 100644 index 0000000..8ec5c0e --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_exec.idx @@ -0,0 +1,37 @@ +nimTitle surreal_exec query_builder/models/surreal/surreal_exec.html module src/allographer/query_builder/models/surreal/surreal_exec 0 +nim get query_builder/models/surreal/surreal_exec.html#get,SurrealQuery proc get(self: SurrealQuery): Future[seq[JsonNode]] 342 +nim first query_builder/models/surreal/surreal_exec.html#first,SurrealQuery proc first(self: SurrealQuery): Future[Option[JsonNode]] 354 +nim find query_builder/models/surreal/surreal_exec.html#find,SurrealQuery,SurrealId,string proc find(self: SurrealQuery; id: SurrealId; key = "id"): Future[Option[JsonNode]] 365 +nim insert query_builder/models/surreal/surreal_exec.html#insert,SurrealQuery,JsonNode proc insert(self: SurrealQuery; items: JsonNode): owned(Future[void]) 378 +nim insert query_builder/models/surreal/surreal_exec.html#insert,SurrealQuery,seq[JsonNode] proc insert(self: SurrealQuery; items: seq[JsonNode]): owned(Future[void]) 385 +nim insertId query_builder/models/surreal/surreal_exec.html#insertId,SurrealQuery,JsonNode,string proc insertId(self: SurrealQuery; items: JsonNode; key = "id"): Future[SurrealId] 392 +nim insertId query_builder/models/surreal/surreal_exec.html#insertId,SurrealQuery,seq[JsonNode],string proc insertId(self: SurrealQuery; items: seq[JsonNode]; key = "id"): Future[\n seq[SurrealId]] 400 +nim insert query_builder/models/surreal/surreal_exec.html#insert,SurrealQuery,T proc insert[T](self: SurrealQuery; items: T): owned(Future[void]) 412 +nim insert query_builder/models/surreal/surreal_exec.html#insert,SurrealQuery,seq[T] proc insert[T](self: SurrealQuery; items: seq[T]): owned(Future[void]) 419 +nim insertId query_builder/models/surreal/surreal_exec.html#insertId,SurrealQuery,T,string proc insertId[T](self: SurrealQuery; items: T; key = "id"): Future[SurrealId] 429 +nim insertId query_builder/models/surreal/surreal_exec.html#insertId,SurrealQuery,seq[T],string proc insertId[T](self: SurrealQuery; items: seq[T]; key = "id"): Future[\n seq[SurrealId]] 437 +nim update query_builder/models/surreal/surreal_exec.html#update,SurrealQuery,JsonNode proc update(self: SurrealQuery; items: JsonNode): owned(Future[void]) 451 +nim update query_builder/models/surreal/surreal_exec.html#update,SurrealConnections,SurrealId,JsonNode proc update(self: SurrealConnections; id: SurrealId; items: JsonNode): owned(\n Future[void]) 458 +nim update query_builder/models/surreal/surreal_exec.html#update,SurrealQuery,T proc update[T](self: SurrealQuery; items: T): owned(Future[void]) 470 +nim update query_builder/models/surreal/surreal_exec.html#update,SurrealConnections,SurrealId,T proc update[T](self: SurrealConnections; id: SurrealId; items: T): owned(\n Future[void]) 477 +nim delete query_builder/models/surreal/surreal_exec.html#delete,SurrealQuery proc delete(self: SurrealQuery): owned(Future[void]) 489 +nim delete query_builder/models/surreal/surreal_exec.html#delete,SurrealQuery,SurrealId proc delete(self: SurrealQuery; id: SurrealId): owned(Future[void]) 496 +nim columns query_builder/models/surreal/surreal_exec.html#columns,SurrealQuery proc columns(self: SurrealQuery): Future[seq[string]] 503 +nim count query_builder/models/surreal/surreal_exec.html#count,SurrealQuery proc count(self: SurrealQuery): Future[int] 519 +nim min query_builder/models/surreal/surreal_exec.html#min,SurrealQuery,string,Collation proc min(self: SurrealQuery; column: string; collaction: Collation = None): Future[\n string] 529 +nim max query_builder/models/surreal/surreal_exec.html#max,SurrealQuery,string,Collation proc max(self: SurrealQuery; column: string; collaction: Collation = None): Future[\n string] 547 +nim avg query_builder/models/surreal/surreal_exec.html#avg,SurrealQuery,string proc avg(self: SurrealQuery; column: string): Future[float] 565 +nim sum query_builder/models/surreal/surreal_exec.html#sum,SurrealQuery,string proc sum(self: SurrealQuery; column: string): Future[float] 584 +nim get query_builder/models/surreal/surreal_exec.html#get,RawSurrealQuery proc get(self: RawSurrealQuery): Future[seq[JsonNode]] 594 +nim exec query_builder/models/surreal/surreal_exec.html#exec,RawSurrealQuery proc exec(self: RawSurrealQuery): owned(Future[void]) 600 +nim info query_builder/models/surreal/surreal_exec.html#info,RawSurrealQuery proc info(self: RawSurrealQuery): Future[JsonNode] 614 +nim first query_builder/models/surreal/surreal_exec.html#first,RawSurrealQuery proc first(self: RawSurrealQuery): Future[Option[JsonNode]] 628 +nim seeder query_builder/models/surreal/surreal_exec.html#seeder.t,SurrealConnections,string,untyped template seeder(rdb: SurrealConnections; tableName: string; body: untyped): untyped 634 +nim seeder query_builder/models/surreal/surreal_exec.html#seeder.t,SurrealConnections,string,string,untyped template seeder(rdb: SurrealConnections; tableName, column: string; body: untyped): untyped 641 +nimgrp insertid query_builder/models/surreal/surreal_exec.html#insertId-procs-all proc 392 +nimgrp delete query_builder/models/surreal/surreal_exec.html#delete-procs-all proc 489 +nimgrp get query_builder/models/surreal/surreal_exec.html#get-procs-all proc 342 +nimgrp update query_builder/models/surreal/surreal_exec.html#update-procs-all proc 451 +nimgrp first query_builder/models/surreal/surreal_exec.html#first-procs-all proc 354 +nimgrp insert query_builder/models/surreal/surreal_exec.html#insert-procs-all proc 378 +nimgrp seeder query_builder/models/surreal/surreal_exec.html#seeder-templates-all template 634 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.html b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.html new file mode 100644 index 0000000..6a89cfb --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.html @@ -0,0 +1,126 @@ + + + + + + + +src/allographer/query_builder/models/surreal/surreal_open + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/surreal/surreal_open

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dbOpen(_: type SurrealDB; namespace: string = ""; database: string = "";
+            user: string = ""; password: string = ""; host: string = "";
+            port: int = 0; maxConnections: int = 1; timeout = 30;
+            shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): Future[
+    SurrealConnections] {....stackTrace: false.}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.idx b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.idx new file mode 100644 index 0000000..510c899 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_open.idx @@ -0,0 +1,2 @@ +nimTitle surreal_open query_builder/models/surreal/surreal_open.html module src/allographer/query_builder/models/surreal/surreal_open 0 +nim dbOpen query_builder/models/surreal/surreal_open.html#dbOpen,typeSurrealDB,string,string,string,string,string,int,int,int,string proc dbOpen(_: type SurrealDB; namespace: string = ""; database: string = "";\n user: string = ""; password: string = ""; host: string = "";\n port: int = 0; maxConnections: int = 1; timeout = 30;\n shouldDisplayLog = false; shouldOutputLogFile = false; logDir = ""): Future[\n SurrealConnections] 42 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.html b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.html new file mode 100644 index 0000000..d4d7b19 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.html @@ -0,0 +1,458 @@ + + + + + + + +src/allographer/query_builder/models/surreal/surreal_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/surreal/surreal_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

https://surrealdb.com/docs/surrealql/operators

+ +
+

Procs

+
+
+
+
proc `distinct`(self: SurrealQuery): SurrealQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc fetch(self: SurrealQuery; columnsArg: varargs[string]): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc groupBy(self: SurrealQuery; column: string): SurrealQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc having(self: SurrealQuery; column: string; symbol: string;
+            value: bool | int | float | string): SurrealQuery
+
+ + + +
+
+
+
proc having(self: SurrealQuery; column: string; symbol: string; value: nil.type): SurrealQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc limit(self: SurrealQuery; num: int): SurrealQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orderBy(self: SurrealQuery; column: string; collation: Collation;
+             order: Order): SurrealQuery {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+
+
proc orderBy(self: SurrealQuery; column: string; order: Order): SurrealQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc orWhere(self: SurrealQuery; column: string; symbol: string;
+             value: bool | int | float | string | SurrealId): SurrealQuery
+
+ + + +
+
+
+
proc orWhere(self: SurrealQuery; column: string; symbol: string; value: nil.type): SurrealQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc parallel(self: SurrealQuery): SurrealQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc raw(self: SurrealConnections; sql: string; arges = newJArray()): RawSurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ +

arges is JArray

+

can't use BLOB data.

+ + +
+
+ +
+
+
+
proc select(self: SurrealConnections; columnsArg: varargs[string]): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc start(self: SurrealQuery; num: int): SurrealQuery {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc table(self: SurrealConnections; tableArg: string): SurrealQuery {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc table(self: SurrealQuery; tableArg: string): SurrealQuery {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc where(self: SurrealQuery; column: string; symbol: string;
+           value: bool | int | float | string | SurrealId): SurrealQuery
+
+ + + +
+
+
+
proc where(self: SurrealQuery; column: string; symbol: string; value: nil.type): SurrealQuery {.
+    ...raises: [CatchableError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc whereBetween(self: SurrealQuery; column: string;
+                  width: array[2, int | float]): SurrealQuery
+
+ +

rdb.table("user").whereBetween("index", [1, 3]).get()

+

SELECT * FROM user WHERE 1 <= index AND index <= 3

+

https://surrealdb.com/docs/surrealql/operators#lessthanorequal

+ + +
+
+ +
+
+
+
proc whereIn(self: SurrealQuery; column: string;
+             width: seq[int | float | string]): SurrealQuery
+
+ +

rdb.table("user").whereIn("index", [2, 3]).get()

+

SELECT * FROM user WHERE [2, 3] CONTAINS index

+

https://surrealdb.com/docs/surrealql/operators#contains

+ + +
+
+ +
+
+
+
proc whereNotBetween(self: SurrealQuery; column: string;
+                     width: array[2, int | float]): SurrealQuery
+
+ +

rdb.table("user").whereNotBetween("index", [1, 3]).get()

+

SELECT * FROM user WHERE index > 1 AND 3 < index

+

https://surrealdb.com/docs/surrealql/operators#greaterthan

+ + +
+
+ +
+
+
+
proc whereNotIn(self: SurrealQuery; column: string;
+                width: seq[int | float | string]): SurrealQuery
+
+ +

rdb.table("user").whereNotIn("index", [2, 3]).get()

+

SELECT * FROM user WHERE [2, 3] CONTAINSNOT index

+

https://surrealdb.com/docs/surrealql/operators#contains-not

+ + +
+
+ +
+
+
+
proc whereNull(self: SurrealQuery; column: string): SurrealQuery {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + https://surrealdb.com/docs/surrealql/operators#equal + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.idx b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.idx new file mode 100644 index 0000000..6c8a586 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_query.idx @@ -0,0 +1,29 @@ +nimTitle surreal_query query_builder/models/surreal/surreal_query.html module src/allographer/query_builder/models/surreal/surreal_query 0 +nim select query_builder/models/surreal/surreal_query.html#select,SurrealConnections,varargs[string] proc select(self: SurrealConnections; columnsArg: varargs[string]): SurrealQuery 11 +nim table query_builder/models/surreal/surreal_query.html#table,SurrealConnections,string proc table(self: SurrealConnections; tableArg: string): SurrealQuery 27 +nim table query_builder/models/surreal/surreal_query.html#table,SurrealQuery,string proc table(self: SurrealQuery; tableArg: string): SurrealQuery 39 +nim `distinct` query_builder/models/surreal/surreal_query.html#distinct,SurrealQuery proc `distinct`(self: SurrealQuery): SurrealQuery 44 +nim where query_builder/models/surreal/surreal_query.html#where,SurrealQuery,string,string, proc where(self: SurrealQuery; column: string; symbol: string;\n value: bool | int | float | string | SurrealId): SurrealQuery 92 +nim where query_builder/models/surreal/surreal_query.html#where,SurrealQuery,string,string,_2 proc where(self: SurrealQuery; column: string; symbol: string; value: nil.type): SurrealQuery 117 +nim orWhere query_builder/models/surreal/surreal_query.html#orWhere,SurrealQuery,string,string, proc orWhere(self: SurrealQuery; column: string; symbol: string;\n value: bool | int | float | string | SurrealId): SurrealQuery 141 +nim orWhere query_builder/models/surreal/surreal_query.html#orWhere,SurrealQuery,string,string,_2 proc orWhere(self: SurrealQuery; column: string; symbol: string; value: nil.type): SurrealQuery 166 +nim whereBetween query_builder/models/surreal/surreal_query.html#whereBetween,SurrealQuery,string,array[,] proc whereBetween(self: SurrealQuery; column: string; width: array[2, int | float]): SurrealQuery 188 +nim whereNotBetween query_builder/models/surreal/surreal_query.html#whereNotBetween,SurrealQuery,string,array[,] proc whereNotBetween(self: SurrealQuery; column: string; width: array[2, int | float]): SurrealQuery 226 +nim whereIn query_builder/models/surreal/surreal_query.html#whereIn,SurrealQuery,string,seq[] proc whereIn(self: SurrealQuery; column: string; width: seq[int | float | string]): SurrealQuery 262 +nim whereNotIn query_builder/models/surreal/surreal_query.html#whereNotIn,SurrealQuery,string,seq[] proc whereNotIn(self: SurrealQuery; column: string; width: seq[int | float | string]): SurrealQuery 281 +nim whereNull query_builder/models/surreal/surreal_query.html#whereNull,SurrealQuery,string proc whereNull(self: SurrealQuery; column: string): SurrealQuery 300 +nim groupBy query_builder/models/surreal/surreal_query.html#groupBy,SurrealQuery,string proc groupBy(self: SurrealQuery; column: string): SurrealQuery 315 +nim having query_builder/models/surreal/surreal_query.html#having,SurrealQuery,string,string, proc having(self: SurrealQuery; column: string; symbol: string;\n value: bool | int | float | string): SurrealQuery 323 +nim having query_builder/models/surreal/surreal_query.html#having,SurrealQuery,string,string,_2 proc having(self: SurrealQuery; column: string; symbol: string; value: nil.type): SurrealQuery 348 +nim fetch query_builder/models/surreal/surreal_query.html#fetch,SurrealQuery,varargs[string] proc fetch(self: SurrealQuery; columnsArg: varargs[string]): SurrealQuery 372 +nim orderBy query_builder/models/surreal/surreal_query.html#orderBy,SurrealQuery,string,Order proc orderBy(self: SurrealQuery; column: string; order: Order): SurrealQuery 377 +nim orderBy query_builder/models/surreal/surreal_query.html#orderBy,SurrealQuery,string,Collation,Order proc orderBy(self: SurrealQuery; column: string; collation: Collation; order: Order): SurrealQuery 393 +nim limit query_builder/models/surreal/surreal_query.html#limit,SurrealQuery,int proc limit(self: SurrealQuery; num: int): SurrealQuery 409 +nim start query_builder/models/surreal/surreal_query.html#start,SurrealQuery,int proc start(self: SurrealQuery; num: int): SurrealQuery 414 +nim parallel query_builder/models/surreal/surreal_query.html#parallel,SurrealQuery proc parallel(self: SurrealQuery): SurrealQuery 419 +nim raw query_builder/models/surreal/surreal_query.html#raw,SurrealConnections,string proc raw(self: SurrealConnections; sql: string; arges = newJArray()): RawSurrealQuery 441 +nimgrp where query_builder/models/surreal/surreal_query.html#where-procs-all proc 92 +nimgrp orderby query_builder/models/surreal/surreal_query.html#orderBy-procs-all proc 377 +nimgrp orwhere query_builder/models/surreal/surreal_query.html#orWhere-procs-all proc 141 +nimgrp having query_builder/models/surreal/surreal_query.html#having-procs-all proc 323 +nimgrp table query_builder/models/surreal/surreal_query.html#table-procs-all proc 27 diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.html b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.html new file mode 100644 index 0000000..72f29ea --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.html @@ -0,0 +1,317 @@ + + + + + + + +src/allographer/query_builder/models/surreal/surreal_types + + + + + + + + + + + + + + + + +
+
+

src/allographer/query_builder/models/surreal/surreal_types

+
+ +
+ +
+ +

created by let rdb = dbOpen(SurrealDB, "ns", "database", "user", "pass", "http://surreal", 8000)

+ +
+

Types

+
+
+
Connection = object
+  conn*: SurrealConn
+  isBusy*: bool
+  createdAt*: int64
+
+ + + +
+
+
+
Connections = ref object
+  conns*: seq[Connection]
+  timeout*: int              ## `getFreeConn` が接続を待つときに積む Future。`returnConn` が先頭から 1 件だけ完了させる。
+  waiters*: Deque[Future[void]]
+
+ + + +
+
+
+
RawSurrealQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+
+ + + +
+
+
+
SurrealConnections = ref object
+  log*: LogSetting
+  pools*: Connections
+
+ + + +
+
+
+
SurrealDB = object
+
+ + + +
+
+
+
SurrealId = object
+  table*: string
+
+ + + +
+
+
+
SurrealQuery = ref object
+  log*: LogSetting
+  pools*: Connections
+  query*: JsonNode
+  queryString*: string
+  placeHolder*: JsonNode
+
+ + created by rdb.select("columnName") or rdb.table("tableName") + +
+
+ +
+
+
+

Procs

+
+
+
+
proc `$`(self: SurrealConnections | SurrealQuery | RawSurrealQuery): string
+
+ + + +
+
+
+
proc `$`(self: SurrealId): string {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc `%`(self: SurrealId): JsonNode {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc new(_: type SurrealId): SurrealId
+
+ + create empty surreal id + +
+
+
+
proc new(_: type SurrealId; rawId: string): SurrealId
+
+ +
let rawId = "user:z7cr4mz474h4ab7rcd6d"
+let surrealId = SureealId.new(rawId)
+ +
+
+
+
proc new(_: type SurrealId; table, id: string): SurrealId
+
+ +
let rawId = "user:z7cr4mz474h4ab7rcd6d"
+let table = "user"
+let id = "z7cr4mz474h4ab7rcd6d"
+let surrealId = SureealId.new(table, id)
+ +
+
+
+
proc new(_: type SurrealQuery; log: LogSetting; pools: Connections;
+         query: JsonNode): SurrealQuery
+
+ + + +
+
+ +
+
+
+
proc rawId(self: SurrealId): string {....raises: [], tags: [], forbids: [].}
+
+ +
let rawId = "user:z7cr4mz474h4ab7rcd6d"
+let surrealId = SureealId.new(rawId)
+surrealId.rawId() == "user:z7cr4mz474h4ab7rcd6d"
+ +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.idx b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.idx new file mode 100644 index 0000000..c3f4cd9 --- /dev/null +++ b/clients/nim-allographer/docs/v2/query_builder/models/surreal/surreal_types.idx @@ -0,0 +1,18 @@ +nimTitle surreal_types query_builder/models/surreal/surreal_types.html module src/allographer/query_builder/models/surreal/surreal_types 0 +nim SurrealDB query_builder/models/surreal/surreal_types.html#SurrealDB object SurrealDB 10 +nim Connection query_builder/models/surreal/surreal_types.html#Connection object Connection 13 +nim Connections query_builder/models/surreal/surreal_types.html#Connections type Connections 19 +nim SurrealConnections query_builder/models/surreal/surreal_types.html#SurrealConnections type SurrealConnections 27 +nim SurrealQuery query_builder/models/surreal/surreal_types.html#SurrealQuery type SurrealQuery 32 +nim new query_builder/models/surreal/surreal_types.html#new,typeSurrealQuery,LogSetting,Connections,JsonNode proc new(_: type SurrealQuery; log: LogSetting; pools: Connections; query: JsonNode): SurrealQuery 42 +nim RawSurrealQuery query_builder/models/surreal/surreal_types.html#RawSurrealQuery type RawSurrealQuery 52 +nim `$` query_builder/models/surreal/surreal_types.html#$ proc `$`(self: SurrealConnections | SurrealQuery | RawSurrealQuery): string 60 +nim SurrealId query_builder/models/surreal/surreal_types.html#SurrealId object SurrealId 64 +nim new query_builder/models/surreal/surreal_types.html#new,typeSurrealId proc new(_: type SurrealId): SurrealId 69 +nim new query_builder/models/surreal/surreal_types.html#new,typeSurrealId,string,string proc new(_: type SurrealId; table, id: string): SurrealId 74 +nim new query_builder/models/surreal/surreal_types.html#new,typeSurrealId,string proc new(_: type SurrealId; rawId: string): SurrealId 87 +nim rawId query_builder/models/surreal/surreal_types.html#rawId,SurrealId proc rawId(self: SurrealId): string 99 +nim `$` query_builder/models/surreal/surreal_types.html#$,SurrealId proc `$`(self: SurrealId): string 107 +nim `%` query_builder/models/surreal/surreal_types.html#%,SurrealId proc `%`(self: SurrealId): JsonNode 111 +nimgrp $ query_builder/models/surreal/surreal_types.html#$-procs-all proc 60 +nimgrp new query_builder/models/surreal/surreal_types.html#new-procs-all proc 42 diff --git a/clients/nim-allographer/docs/v2/schema_builder.html b/clients/nim-allographer/docs/v2/schema_builder.html new file mode 100644 index 0000000..93e63a4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder.html @@ -0,0 +1,94 @@ + + + + + + + +src/allographer/schema_builder + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ + + +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder.idx b/clients/nim-allographer/docs/v2/schema_builder.idx new file mode 100644 index 0000000..3830560 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder.idx @@ -0,0 +1 @@ +nimTitle schema_builder schema_builder.html module src/allographer/schema_builder 0 diff --git a/clients/nim-allographer/docs/v2/schema_builder/enums.html b/clients/nim-allographer/docs/v2/schema_builder/enums.html new file mode 100644 index 0000000..196d059 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/enums.html @@ -0,0 +1,176 @@ + + + + + + + +src/allographer/schema_builder/enums + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/enums

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Types

+
+
+
ColumnMigrationType = enum
+  AddColumn, ChangeColumn, RenameColumn, DropColumn
+
+ + + +
+
+
+
DefaultDateTime = enum
+  None, Current, CurrentOnUpdate
+
+ + + +
+
+
+
ForeignOnDelete = enum
+  RESTRICT, CASCADE, SET_NULL, NO_ACTION
+
+ + + +
+
+
+
RdbTypeKind = enum
+  rdbIncrements = "rdbIncrements", rdbInteger = "rdbInteger",
+  rdbSmallInteger = "rdbSmallInteger", rdbMediumInteger = "rdbMediumInteger",
+  rdbBigInteger = "rdbBigInteger", rdbDecimal = "rdbDecimal",
+  rdbDouble = "rdbDouble", rdbFloat = "rdbFloat", rdbUuid = "rdbUuid",
+  rdbChar = "rdbChar", rdbString = "rdbString", rdbText = "rdbText",
+  rdbMediumText = "rdbMediumText", rdbLongText = "rdbLongText",
+  rdbDate = "rdbDate", rdbDatetime = "rdbDatetime", rdbTime = "rdbTime",
+  rdbTimestamp = "rdbTimestamp", rdbTimestamps = "rdbTimestamps",
+  rdbSoftDelete = "rdbSoftDelete", rdbBinary = "rdbBinary",
+  rdbBoolean = "rdbBoolean", rdbEnumField = "rdbEnumField", rdbJson = "rdbJson",
+  rdbForeign = "rdbForeign", rdbStrForeign = "rdbStrForeign"
+
+ + + +
+
+
+
TableMigrationType = enum
+  CreateTable, ChangeTable, RenameTable, DropTable
+
+ + + +
+
+
+
UsecaseType = enum
+  Create, Alter, Drop
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/enums.idx b/clients/nim-allographer/docs/v2/schema_builder/enums.idx new file mode 100644 index 0000000..982fadc --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/enums.idx @@ -0,0 +1,51 @@ +nimTitle enums schema_builder/enums.html module src/allographer/schema_builder/enums 0 +nim rdbIncrements schema_builder/enums.html#rdbIncrements RdbTypeKind.rdbIncrements 1 +nim rdbInteger schema_builder/enums.html#rdbInteger RdbTypeKind.rdbInteger 1 +nim rdbSmallInteger schema_builder/enums.html#rdbSmallInteger RdbTypeKind.rdbSmallInteger 1 +nim rdbMediumInteger schema_builder/enums.html#rdbMediumInteger RdbTypeKind.rdbMediumInteger 1 +nim rdbBigInteger schema_builder/enums.html#rdbBigInteger RdbTypeKind.rdbBigInteger 1 +nim rdbDecimal schema_builder/enums.html#rdbDecimal RdbTypeKind.rdbDecimal 1 +nim rdbDouble schema_builder/enums.html#rdbDouble RdbTypeKind.rdbDouble 1 +nim rdbFloat schema_builder/enums.html#rdbFloat RdbTypeKind.rdbFloat 1 +nim rdbUuid schema_builder/enums.html#rdbUuid RdbTypeKind.rdbUuid 1 +nim rdbChar schema_builder/enums.html#rdbChar RdbTypeKind.rdbChar 1 +nim rdbString schema_builder/enums.html#rdbString RdbTypeKind.rdbString 1 +nim rdbText schema_builder/enums.html#rdbText RdbTypeKind.rdbText 1 +nim rdbMediumText schema_builder/enums.html#rdbMediumText RdbTypeKind.rdbMediumText 1 +nim rdbLongText schema_builder/enums.html#rdbLongText RdbTypeKind.rdbLongText 1 +nim rdbDate schema_builder/enums.html#rdbDate RdbTypeKind.rdbDate 1 +nim rdbDatetime schema_builder/enums.html#rdbDatetime RdbTypeKind.rdbDatetime 1 +nim rdbTime schema_builder/enums.html#rdbTime RdbTypeKind.rdbTime 1 +nim rdbTimestamp schema_builder/enums.html#rdbTimestamp RdbTypeKind.rdbTimestamp 1 +nim rdbTimestamps schema_builder/enums.html#rdbTimestamps RdbTypeKind.rdbTimestamps 1 +nim rdbSoftDelete schema_builder/enums.html#rdbSoftDelete RdbTypeKind.rdbSoftDelete 1 +nim rdbBinary schema_builder/enums.html#rdbBinary RdbTypeKind.rdbBinary 1 +nim rdbBoolean schema_builder/enums.html#rdbBoolean RdbTypeKind.rdbBoolean 1 +nim rdbEnumField schema_builder/enums.html#rdbEnumField RdbTypeKind.rdbEnumField 1 +nim rdbJson schema_builder/enums.html#rdbJson RdbTypeKind.rdbJson 1 +nim rdbForeign schema_builder/enums.html#rdbForeign RdbTypeKind.rdbForeign 1 +nim rdbStrForeign schema_builder/enums.html#rdbStrForeign RdbTypeKind.rdbStrForeign 1 +nim RdbTypeKind schema_builder/enums.html#RdbTypeKind enum RdbTypeKind 1 +nim RESTRICT schema_builder/enums.html#RESTRICT ForeignOnDelete.RESTRICT 35 +nim CASCADE schema_builder/enums.html#CASCADE ForeignOnDelete.CASCADE 35 +nim SET_NULL schema_builder/enums.html#SET_NULL ForeignOnDelete.SET_NULL 35 +nim NO_ACTION schema_builder/enums.html#NO_ACTION ForeignOnDelete.NO_ACTION 35 +nim ForeignOnDelete schema_builder/enums.html#ForeignOnDelete enum ForeignOnDelete 35 +nim None schema_builder/enums.html#None DefaultDateTime.None 42 +nim Current schema_builder/enums.html#Current DefaultDateTime.Current 42 +nim CurrentOnUpdate schema_builder/enums.html#CurrentOnUpdate DefaultDateTime.CurrentOnUpdate 42 +nim DefaultDateTime schema_builder/enums.html#DefaultDateTime enum DefaultDateTime 42 +nim Create schema_builder/enums.html#Create UsecaseType.Create 47 +nim Alter schema_builder/enums.html#Alter UsecaseType.Alter 47 +nim Drop schema_builder/enums.html#Drop UsecaseType.Drop 47 +nim UsecaseType schema_builder/enums.html#UsecaseType enum UsecaseType 47 +nim CreateTable schema_builder/enums.html#CreateTable TableMigrationType.CreateTable 52 +nim ChangeTable schema_builder/enums.html#ChangeTable TableMigrationType.ChangeTable 52 +nim RenameTable schema_builder/enums.html#RenameTable TableMigrationType.RenameTable 52 +nim DropTable schema_builder/enums.html#DropTable TableMigrationType.DropTable 52 +nim TableMigrationType schema_builder/enums.html#TableMigrationType enum TableMigrationType 52 +nim AddColumn schema_builder/enums.html#AddColumn ColumnMigrationType.AddColumn 58 +nim ChangeColumn schema_builder/enums.html#ChangeColumn ColumnMigrationType.ChangeColumn 58 +nim RenameColumn schema_builder/enums.html#RenameColumn ColumnMigrationType.RenameColumn 58 +nim DropColumn schema_builder/enums.html#DropColumn ColumnMigrationType.DropColumn 58 +nim ColumnMigrationType schema_builder/enums.html#ColumnMigrationType enum ColumnMigrationType 58 diff --git a/clients/nim-allographer/docs/v2/schema_builder/models/column.html b/clients/nim-allographer/docs/v2/schema_builder/models/column.html new file mode 100644 index 0000000..e540442 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/models/column.html @@ -0,0 +1,855 @@ + + + + + + + +src/allographer/schema_builder/models/column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/models/column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Imports

+
+ ../enums +
+
+
+

Types

+
+
+
Column = ref object
+  name*: string
+  typ*: RdbTypeKind
+  isIndex*: bool
+  isNullable*: bool
+  isUnsigned*: bool
+  isUnique*: bool
+  isAutoIncrement*: bool
+  isDefault*: bool
+  isUpdatedAt*: bool
+  defaultBool*: bool
+  defaultInt*: int
+  defaultFloat*: float
+  defaultString*: string
+  defaultJson*: JsonNode
+  defaultDatetime*: DefaultDateTime
+  foreignOnDelete*: ForeignOnDelete
+  commentContent*: string
+  isCommentNull*: bool
+  info*: JsonNode
+  checksum*: string
+  previousName*: string
+  migrationType*: ColumnMigrationType
+  usecaseType*: UsecaseType
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc add(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc autoIncrement(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc bigInteger(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc binary(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc boolean(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc change(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc char(_: type Column; name: string; maxLength: int): Column
+
+ + + +
+
+ +
+
+
+
proc comment(c: Column; arg: type nil): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+
+
proc comment(c: Column; value: string): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc date(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc datetime(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc decimal(_: type Column; name: string; maximum: int; digit: int): Column
+
+ + + +
+
+ +
+
+
+
proc default(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc default(c: Column; value: bool): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc default(c: Column; value: DefaultDateTime): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+
+
proc default(c: Column; value: float): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+
+
proc default(c: Column; value: int): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc default(c: Column; value: JsonNode): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+
+
proc default(c: Column; value: string): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc double(_: type Column; name: string; maximum: int; digit: int): Column
+
+ + + +
+
+ +
+
+
+
proc dropColumn(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc enumField(_: type Column; name: string; options: openArray[string]): Column
+
+ + + +
+
+ +
+
+
+
proc float(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc foreign(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc increments(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc index(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc integer(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc json(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc longText(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc mediumInteger(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc mediumText(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc nullable(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc onDelete(self: Column; kind: ForeignOnDelete): Column {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc onTable(self: Column; table: string): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc reference(self: Column; column: string): Column {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc renameColumn(_: type Column; src, dest: string): Column
+
+ + + +
+
+ +
+
+
+
proc smallInteger(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc softDelete(_: type Column): Column
+
+ + + +
+
+ +
+
+
+
proc strForeign(_: type Column; name: string; length = 256): Column
+
+ + + +
+
+ +
+
+
+
proc string(_: type Column; name: string; length = 256): Column
+
+ + + +
+
+ +
+
+
+
proc text(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc time(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc timestamp(_: type Column; name: string): Column
+
+ + + +
+
+ +
+
+
+
proc timestamps(_: type Column): Column
+
+ + + +
+
+ +
+
+
+
proc toSchema(self: Column): JsonNode {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc unique(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc unsigned(c: Column): Column {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc uuid(_: type Column; name: string): Column
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/models/column.idx b/clients/nim-allographer/docs/v2/schema_builder/models/column.idx new file mode 100644 index 0000000..f8add04 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/models/column.idx @@ -0,0 +1,52 @@ +nimTitle column schema_builder/models/column.html module src/allographer/schema_builder/models/column 0 +nim Column schema_builder/models/column.html#Column type Column 5 +nim toSchema schema_builder/models/column.html#toSchema,Column proc toSchema(self: Column): JsonNode 37 +nim increments schema_builder/models/column.html#increments,typeColumn,string proc increments(_: type Column; name: string): Column 65 +nim integer schema_builder/models/column.html#integer,typeColumn,string proc integer(_: type Column; name: string): Column 72 +nim smallInteger schema_builder/models/column.html#smallInteger,typeColumn,string proc smallInteger(_: type Column; name: string): Column 79 +nim mediumInteger schema_builder/models/column.html#mediumInteger,typeColumn,string proc mediumInteger(_: type Column; name: string): Column 86 +nim bigInteger schema_builder/models/column.html#bigInteger,typeColumn,string proc bigInteger(_: type Column; name: string): Column 93 +nim decimal schema_builder/models/column.html#decimal,typeColumn,string,int,int proc decimal(_: type Column; name: string; maximum: int; digit: int): Column 103 +nim double schema_builder/models/column.html#double,typeColumn,string,int,int proc double(_: type Column; name: string; maximum: int; digit: int): Column 114 +nim float schema_builder/models/column.html#float,typeColumn,string proc float(_: type Column; name: string): Column 125 +nim char schema_builder/models/column.html#char,typeColumn,string,int proc char(_: type Column; name: string; maxLength: int): Column 135 +nim string schema_builder/models/column.html#string,typeColumn,string,int proc string(_: type Column; name: string; length = 256): Column 145 +nim uuid schema_builder/models/column.html#uuid,typeColumn,string proc uuid(_: type Column; name: string): Column 153 +nim text schema_builder/models/column.html#text,typeColumn,string proc text(_: type Column; name: string): Column 162 +nim mediumText schema_builder/models/column.html#mediumText,typeColumn,string proc mediumText(_: type Column; name: string): Column 169 +nim longText schema_builder/models/column.html#longText,typeColumn,string proc longText(_: type Column; name: string): Column 176 +nim date schema_builder/models/column.html#date,typeColumn,string proc date(_: type Column; name: string): Column 186 +nim datetime schema_builder/models/column.html#datetime,typeColumn,string proc datetime(_: type Column; name: string): Column 193 +nim time schema_builder/models/column.html#time,typeColumn,string proc time(_: type Column; name: string): Column 200 +nim timestamp schema_builder/models/column.html#timestamp,typeColumn,string proc timestamp(_: type Column; name: string): Column 207 +nim timestamps schema_builder/models/column.html#timestamps,typeColumn proc timestamps(_: type Column): Column 214 +nim softDelete schema_builder/models/column.html#softDelete,typeColumn proc softDelete(_: type Column): Column 221 +nim binary schema_builder/models/column.html#binary,typeColumn,string proc binary(_: type Column; name: string): Column 230 +nim boolean schema_builder/models/column.html#boolean,typeColumn,string proc boolean(_: type Column; name: string): Column 237 +nim enumField schema_builder/models/column.html#enumField,typeColumn,string,openArray[string] proc enumField(_: type Column; name: string; options: openArray[string]): Column 244 +nim json schema_builder/models/column.html#json,typeColumn,string proc json(_: type Column; name: string): Column 254 +nim foreign schema_builder/models/column.html#foreign,typeColumn,string proc foreign(_: type Column; name: string): Column 264 +nim strForeign schema_builder/models/column.html#strForeign,typeColumn,string,int proc strForeign(_: type Column; name: string; length = 256): Column 273 +nim reference schema_builder/models/column.html#reference,Column,string proc reference(self: Column; column: string): Column 283 +nim onTable schema_builder/models/column.html#onTable,Column,string proc onTable(self: Column; table: string): Column 291 +nim onDelete schema_builder/models/column.html#onDelete,Column,ForeignOnDelete proc onDelete(self: Column; kind: ForeignOnDelete): Column 305 +nim autoIncrement schema_builder/models/column.html#autoIncrement,Column proc autoIncrement(c: Column): Column 313 +nim default schema_builder/models/column.html#default,Column,bool proc default(c: Column; value: bool): Column 320 +nim default schema_builder/models/column.html#default,Column,int proc default(c: Column; value: int): Column 327 +nim default schema_builder/models/column.html#default,Column,float proc default(c: Column; value: float): Column 334 +nim default schema_builder/models/column.html#default,Column,string proc default(c: Column; value: string): Column 341 +nim default schema_builder/models/column.html#default,Column,JsonNode proc default(c: Column; value: JsonNode): Column 348 +nim default schema_builder/models/column.html#default,Column,DefaultDateTime proc default(c: Column; value: DefaultDateTime): Column 355 +nim default schema_builder/models/column.html#default,Column proc default(c: Column): Column 362 +nim index schema_builder/models/column.html#index,Column proc index(c: Column): Column 368 +nim nullable schema_builder/models/column.html#nullable,Column proc nullable(c: Column): Column 374 +nim unique schema_builder/models/column.html#unique,Column proc unique(c: Column): Column 379 +nim unsigned schema_builder/models/column.html#unsigned,Column proc unsigned(c: Column): Column 385 +nim comment schema_builder/models/column.html#comment,Column,string proc comment(c: Column; value: string): Column 390 +nim comment schema_builder/models/column.html#comment,Column,type proc comment(c: Column; arg: type nil): Column 394 +nim add schema_builder/models/column.html#add,Column proc add(c: Column): Column 402 +nim change schema_builder/models/column.html#change,Column proc change(c: Column): Column 406 +nim renameColumn schema_builder/models/column.html#renameColumn,typeColumn,string,string proc renameColumn(_: type Column; src, dest: string): Column 410 +nim dropColumn schema_builder/models/column.html#dropColumn,typeColumn,string proc dropColumn(_: type Column; name: string): Column 417 +nimgrp default schema_builder/models/column.html#default-procs-all proc 320 +nimgrp comment schema_builder/models/column.html#comment-procs-all proc 390 diff --git a/clients/nim-allographer/docs/v2/schema_builder/models/table.html b/clients/nim-allographer/docs/v2/schema_builder/models/table.html new file mode 100644 index 0000000..b68e607 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/models/table.html @@ -0,0 +1,223 @@ + + + + + + + +src/allographer/schema_builder/models/table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/models/table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Imports

+
+ ../enums, column +
+
+
+

Types

+
+
+
Table = ref object
+  name*: string
+  columns*: seq[Column]
+  primary*: seq[string]
+  query*: seq[string]
+  commentContent*: string
+  checksum*: string
+  previousName*: string
+  migrationType*: TableMigrationType
+  usecaseType*: UsecaseType
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc renameTo(self: Table; name: string): Table {....raises: [], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc smallName(self: Table): string {....raises: [], tags: [], forbids: [].}
+
+ + TableName -> tablename + +
+
+ +
+
+
+
proc table(name: string): Table {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc table(name: string; columns: openArray[Column]; comment = ""): Table {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc table(name: string; columns: seq[Column]; primary: seq[string] = @[];
+           comment = ""): Table {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc toSchema(self: Table): JsonNode {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/models/table.idx b/clients/nim-allographer/docs/v2/schema_builder/models/table.idx new file mode 100644 index 0000000..2483d61 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/models/table.idx @@ -0,0 +1,9 @@ +nimTitle table schema_builder/models/table.html module src/allographer/schema_builder/models/table 0 +nim Table schema_builder/models/table.html#Table type Table 8 +nim table schema_builder/models/table.html#table,string proc table(name: string): Table 20 +nim table schema_builder/models/table.html#table,string,openArray[Column],string proc table(name: string; columns: openArray[Column]; comment = ""): Table 28 +nim table schema_builder/models/table.html#table,string,seq[Column],seq[string],string proc table(name: string; columns: seq[Column]; primary: seq[string] = @[];\n comment = ""): Table 37 +nim toSchema schema_builder/models/table.html#toSchema,Table proc toSchema(self: Table): JsonNode 48 +nim smallName schema_builder/models/table.html#smallName,Table proc smallName(self: Table): string 63 +nim renameTo schema_builder/models/table.html#renameTo,Table,string proc renameTo(self: Table; name: string): Table 67 +nimgrp table schema_builder/models/table.html#table-procs-all proc 20 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.html new file mode 100644 index 0000000..0bf3d28 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/add_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/add_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addColumn(self: MariadbSchema; isReset: bool) {....raises: [KeyError, DbError,
+    ValueError, Exception, OSError, IOError, CatchableError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.idx new file mode 100644 index 0000000..440c279 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/add_column.idx @@ -0,0 +1,2 @@ +nimTitle add_column schema_builder/queries/mariadb/add_column.html module src/allographer/schema_builder/queries/mariadb/add_column 0 +nim addColumn schema_builder/queries/mariadb/add_column.html#addColumn,MariadbSchema,bool proc addColumn(self: MariadbSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.html new file mode 100644 index 0000000..d34ccce --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/change_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/change_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc changeColumn(self: MariadbSchema; isReset: bool) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.idx new file mode 100644 index 0000000..80c8420 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/change_column.idx @@ -0,0 +1,2 @@ +nimTitle change_column schema_builder/queries/mariadb/change_column.html module src/allographer/schema_builder/queries/mariadb/change_column 0 +nim changeColumn schema_builder/queries/mariadb/change_column.html#changeColumn,MariadbSchema,bool proc changeColumn(self: MariadbSchema; isReset: bool) 15 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.html new file mode 100644 index 0000000..0be1958 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/create_migration_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/create_migration_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createMigrationTable(self: MariadbSchema) {.
+    ...raises: [KeyError, DbError, ValueError, Exception, OSError, IOError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.idx new file mode 100644 index 0000000..d2e4a2c --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_migration_table.idx @@ -0,0 +1,2 @@ +nimTitle create_migration_table schema_builder/queries/mariadb/create_migration_table.html module src/allographer/schema_builder/queries/mariadb/create_migration_table 0 +nim createMigrationTable schema_builder/queries/mariadb/create_migration_table.html#createMigrationTable,MariadbSchema proc createMigrationTable(self: MariadbSchema) 10 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.html new file mode 100644 index 0000000..85cf017 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/create_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/create_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createTable(self: MariadbSchema; isReset: bool) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.idx new file mode 100644 index 0000000..7e713e4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/create_table.idx @@ -0,0 +1,2 @@ +nimTitle create_table schema_builder/queries/mariadb/create_table.html module src/allographer/schema_builder/queries/mariadb/create_table 0 +nim createTable schema_builder/queries/mariadb/create_table.html#createTable,MariadbSchema,bool proc createTable(self: MariadbSchema; isReset: bool) 18 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.html new file mode 100644 index 0000000..6ad42aa --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/drop_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/drop_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropColumn(self: MariadbSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.idx new file mode 100644 index 0000000..92b238e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_column.idx @@ -0,0 +1,2 @@ +nimTitle drop_column schema_builder/queries/mariadb/drop_column.html module src/allographer/schema_builder/queries/mariadb/drop_column 0 +nim dropColumn schema_builder/queries/mariadb/drop_column.html#dropColumn,MariadbSchema,bool proc dropColumn(self: MariadbSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.html new file mode 100644 index 0000000..8872ddd --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/drop_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/drop_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropTable(self: MariadbSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.idx new file mode 100644 index 0000000..2890b63 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/drop_table.idx @@ -0,0 +1,2 @@ +nimTitle drop_table schema_builder/queries/mariadb/drop_table.html module src/allographer/schema_builder/queries/mariadb/drop_table 0 +nim dropTable schema_builder/queries/mariadb/drop_table.html#dropTable,MariadbSchema,bool proc dropTable(self: MariadbSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.html new file mode 100644 index 0000000..64e29f4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.html @@ -0,0 +1,153 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/mariadb_query_type + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/mariadb_query_type

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
MariadbSchema = ref object
+  rdb*: MariadbConnections
+  table*: Table
+  column*: Column
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc new(_: type MariadbSchema; rdb: MariadbConnections; table: Table): MariadbSchema
+
+ + + +
+
+
+
proc new(_: type MariadbSchema; rdb: MariadbConnections; table: Table;
+         column: Column): MariadbSchema
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.idx new file mode 100644 index 0000000..1238fa7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/mariadb_query_type.idx @@ -0,0 +1,5 @@ +nimTitle mariadb_query_type schema_builder/queries/mariadb/mariadb_query_type.html module src/allographer/schema_builder/queries/mariadb/mariadb_query_type 0 +nim MariadbSchema schema_builder/queries/mariadb/mariadb_query_type.html#MariadbSchema type MariadbSchema 6 +nim new schema_builder/queries/mariadb/mariadb_query_type.html#new,typeMariadbSchema,MariadbConnections,Table proc new(_: type MariadbSchema; rdb: MariadbConnections; table: Table): MariadbSchema 11 +nim new schema_builder/queries/mariadb/mariadb_query_type.html#new,typeMariadbSchema,MariadbConnections,Table,Column proc new(_: type MariadbSchema; rdb: MariadbConnections; table: Table; column: Column): MariadbSchema 14 +nimgrp new schema_builder/queries/mariadb/mariadb_query_type.html#new-procs-all proc 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.html new file mode 100644 index 0000000..ee7b555 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/rename_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/rename_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameColumn(self: MariadbSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.idx new file mode 100644 index 0000000..3724832 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_column.idx @@ -0,0 +1,2 @@ +nimTitle rename_column schema_builder/queries/mariadb/rename_column.html module src/allographer/schema_builder/queries/mariadb/rename_column 0 +nim renameColumn schema_builder/queries/mariadb/rename_column.html#renameColumn,MariadbSchema,bool proc renameColumn(self: MariadbSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.html new file mode 100644 index 0000000..b254fa3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/rename_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/rename_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameTable(self: MariadbSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.idx new file mode 100644 index 0000000..9157b08 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/rename_table.idx @@ -0,0 +1,2 @@ +nimTitle rename_table schema_builder/queries/mariadb/rename_table.html module src/allographer/schema_builder/queries/mariadb/rename_table 0 +nim renameTable schema_builder/queries/mariadb/rename_table.html#renameTable,MariadbSchema,bool proc renameTable(self: MariadbSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.html new file mode 100644 index 0000000..a233c0e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.html @@ -0,0 +1,135 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/reset_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/reset_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc resetMigrationTable(self: MariadbSchema) {....raises: [ValueError, Exception,
+    OSError, KeyError, IOError, DbError, CatchableError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc resetTable(self: MariadbSchema) {....raises: [ValueError, Exception, OSError,
+    IOError, DbError], tags: [TimeEffect, RootEffect, WriteDirEffect,
+                              ReadDirEffect, ReadIOEffect, WriteIOEffect],
+                                       forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.idx new file mode 100644 index 0000000..ea59888 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/reset_table.idx @@ -0,0 +1,3 @@ +nimTitle reset_table schema_builder/queries/mariadb/reset_table.html module src/allographer/schema_builder/queries/mariadb/reset_table 0 +nim resetMigrationTable schema_builder/queries/mariadb/reset_table.html#resetMigrationTable,MariadbSchema proc resetMigrationTable(self: MariadbSchema) 9 +nim resetTable schema_builder/queries/mariadb/reset_table.html#resetTable,MariadbSchema proc resetTable(self: MariadbSchema) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.html new file mode 100644 index 0000000..c6f551e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.html @@ -0,0 +1,202 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/schema_utils + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/schema_utils

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(rdb: MariadbConnections; queries: seq[string]) {.
+    ...raises: [ValueError, Exception, OSError, IOError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execThenSaveHistory(rdb: MariadbConnections; tableName: string;
+                         queries: seq[string]; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+
+
proc execThenSaveHistory(rdb: MariadbConnections; tableName: string;
+                         query: string; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc notAllowedOption(option, typ, column: string) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ + {option} is not allowed in {typ} column {column} + +
+
+ +
+
+
+
proc notAllowedType(typ: string) {....raises: [DbError], tags: [], forbids: [].}
+
+ + Change to {typ} type is not allowed + +
+
+ +
+
+
+
proc shouldRun(rdb: MariadbConnections; table: Table; checksum: string;
+               isReset: bool): bool {....raises: [ValueError, Exception, OSError,
+    KeyError, IOError, CatchableError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.idx new file mode 100644 index 0000000..22887ac --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/schema_utils.idx @@ -0,0 +1,8 @@ +nimTitle schema_utils schema_builder/queries/mariadb/schema_utils.html module src/allographer/schema_builder/queries/mariadb/schema_utils 0 +nim notAllowedOption schema_builder/queries/mariadb/schema_utils.html#notAllowedOption,string,string,string proc notAllowedOption(option, typ, column: string) 14 +nim notAllowedType schema_builder/queries/mariadb/schema_utils.html#notAllowedType,string proc notAllowedType(typ: string) 18 +nim shouldRun schema_builder/queries/mariadb/schema_utils.html#shouldRun,MariadbConnections,Table,string,bool proc shouldRun(rdb: MariadbConnections; table: Table; checksum: string; isReset: bool): bool 27 +nim exec schema_builder/queries/mariadb/schema_utils.html#exec,MariadbConnections,seq[string] proc exec(rdb: MariadbConnections; queries: seq[string]) 38 +nim execThenSaveHistory schema_builder/queries/mariadb/schema_utils.html#execThenSaveHistory,MariadbConnections,string,seq[string],string proc execThenSaveHistory(rdb: MariadbConnections; tableName: string;\n queries: seq[string]; checksum: string) 56 +nim execThenSaveHistory schema_builder/queries/mariadb/schema_utils.html#execThenSaveHistory,MariadbConnections,string,string,string proc execThenSaveHistory(rdb: MariadbConnections; tableName: string; query: string;\n checksum: string) 85 +nimgrp execthensavehistory schema_builder/queries/mariadb/schema_utils.html#execThenSaveHistory-procs-all proc 56 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.html new file mode 100644 index 0000000..39feb13 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.html @@ -0,0 +1,115 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/sub/add_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/sub/add_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addColumnString(table: Table; column: Column): seq[string] {.
+    ...raises: [KeyError, DbError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.idx new file mode 100644 index 0000000..58ec429 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/add_column_query.idx @@ -0,0 +1,2 @@ +nimTitle add_column_query schema_builder/queries/mariadb/sub/add_column_query.html module src/allographer/schema_builder/queries/mariadb/sub/add_column_query 0 +nim addColumnString schema_builder/queries/mariadb/sub/add_column_query.html#addColumnString,Table,Column proc addColumnString(table: Table; column: Column): seq[string] 461 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.html new file mode 100644 index 0000000..4a7d213 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.html @@ -0,0 +1,147 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/sub/change_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/sub/change_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addIndexString(column: Column; table: Table): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc changeColumnString(table: Table; column: Column): seq[string] {.
+    ...raises: [DbError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc changeForeignKey(column: Column; table: Table): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.idx new file mode 100644 index 0000000..acb771a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/change_column_query.idx @@ -0,0 +1,4 @@ +nimTitle change_column_query schema_builder/queries/mariadb/sub/change_column_query.html module src/allographer/schema_builder/queries/mariadb/sub/change_column_query 0 +nim changeForeignKey schema_builder/queries/mariadb/sub/change_column_query.html#changeForeignKey,Column,Table proc changeForeignKey(column: Column; table: Table): string 440 +nim addIndexString schema_builder/queries/mariadb/sub/change_column_query.html#addIndexString,Column,Table proc addIndexString(column: Column; table: Table): string 457 +nim changeColumnString schema_builder/queries/mariadb/sub/change_column_query.html#changeColumnString,Table,Column proc changeColumnString(table: Table; column: Column): seq[string] 461 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.html new file mode 100644 index 0000000..93a9e12 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.html @@ -0,0 +1,163 @@ + + + + + + + +src/allographer/schema_builder/queries/mariadb/sub/create_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mariadb/sub/create_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc alterAddForeignKey(column: Column; table: Table): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createColumnString(table: Table; column: Column): string {.
+    ...raises: [KeyError, DbError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createForeignKey(table: Table; column: Column): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createIndexString(table: Table; column: Column): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.idx new file mode 100644 index 0000000..d675dbf --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mariadb/sub/create_column_query.idx @@ -0,0 +1,5 @@ +nimTitle create_column_query schema_builder/queries/mariadb/sub/create_column_query.html module src/allographer/schema_builder/queries/mariadb/sub/create_column_query 0 +nim createForeignKey schema_builder/queries/mariadb/sub/create_column_query.html#createForeignKey,Table,Column proc createForeignKey(table: Table; column: Column): string 492 +nim alterAddForeignKey schema_builder/queries/mariadb/sub/create_column_query.html#alterAddForeignKey,Column,Table proc alterAddForeignKey(column: Column; table: Table): string 508 +nim createIndexString schema_builder/queries/mariadb/sub/create_column_query.html#createIndexString,Table,Column proc createIndexString(table: Table; column: Column): string 535 +nim createColumnString schema_builder/queries/mariadb/sub/create_column_query.html#createColumnString,Table,Column proc createColumnString(table: Table; column: Column): string 539 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.html new file mode 100644 index 0000000..b8c0d92 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/add_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/add_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addColumn(self: MysqlSchema; isReset: bool) {....raises: [KeyError, DbError,
+    ValueError, Exception, OSError, IOError, CatchableError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.idx new file mode 100644 index 0000000..ecd056b --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/add_column.idx @@ -0,0 +1,2 @@ +nimTitle add_column schema_builder/queries/mysql/add_column.html module src/allographer/schema_builder/queries/mysql/add_column 0 +nim addColumn schema_builder/queries/mysql/add_column.html#addColumn,MysqlSchema,bool proc addColumn(self: MysqlSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.html new file mode 100644 index 0000000..52999bf --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/change_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/change_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc changeColumn(self: MysqlSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, IOError, DbError, JsonParsingError, KeyError,
+    CatchableError], tags: [TimeEffect, RootEffect, WriteDirEffect,
+                            ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.idx new file mode 100644 index 0000000..4627586 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/change_column.idx @@ -0,0 +1,2 @@ +nimTitle change_column schema_builder/queries/mysql/change_column.html module src/allographer/schema_builder/queries/mysql/change_column 0 +nim changeColumn schema_builder/queries/mysql/change_column.html#changeColumn,MysqlSchema,bool proc changeColumn(self: MysqlSchema; isReset: bool) 17 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.html new file mode 100644 index 0000000..611f0d0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/create_migration_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/create_migration_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createMigrationTable(self: MysqlSchema) {....raises: [KeyError, DbError,
+    ValueError, Exception, OSError, IOError, JsonParsingError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.idx new file mode 100644 index 0000000..fd180fa --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_migration_table.idx @@ -0,0 +1,2 @@ +nimTitle create_migration_table schema_builder/queries/mysql/create_migration_table.html module src/allographer/schema_builder/queries/mysql/create_migration_table 0 +nim createMigrationTable schema_builder/queries/mysql/create_migration_table.html#createMigrationTable,MysqlSchema proc createMigrationTable(self: MysqlSchema) 15 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.html new file mode 100644 index 0000000..3108a34 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/create_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/create_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createTable(self: MysqlSchema; isReset: bool) {....raises: [KeyError, DbError,
+    ValueError, Exception, OSError, IOError, JsonParsingError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.idx new file mode 100644 index 0000000..e4b7136 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/create_table.idx @@ -0,0 +1,2 @@ +nimTitle create_table schema_builder/queries/mysql/create_table.html module src/allographer/schema_builder/queries/mysql/create_table 0 +nim createTable schema_builder/queries/mysql/create_table.html#createTable,MysqlSchema,bool proc createTable(self: MysqlSchema; isReset: bool) 21 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.html new file mode 100644 index 0000000..c128918 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/drop_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/drop_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropColumn(self: MysqlSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.idx new file mode 100644 index 0000000..4065398 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_column.idx @@ -0,0 +1,2 @@ +nimTitle drop_column schema_builder/queries/mysql/drop_column.html module src/allographer/schema_builder/queries/mysql/drop_column 0 +nim dropColumn schema_builder/queries/mysql/drop_column.html#dropColumn,MysqlSchema,bool proc dropColumn(self: MysqlSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.html new file mode 100644 index 0000000..29d2d90 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/drop_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/drop_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropTable(self: MysqlSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.idx new file mode 100644 index 0000000..e9c13a0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/drop_table.idx @@ -0,0 +1,2 @@ +nimTitle drop_table schema_builder/queries/mysql/drop_table.html module src/allographer/schema_builder/queries/mysql/drop_table 0 +nim dropTable schema_builder/queries/mysql/drop_table.html#dropTable,MysqlSchema,bool proc dropTable(self: MysqlSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.html new file mode 100644 index 0000000..ef81f8a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.html @@ -0,0 +1,153 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/mysql_query_type + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/mysql_query_type

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
MysqlSchema = ref object
+  rdb*: MysqlConnections
+  table*: Table
+  column*: Column
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc new(_: type MysqlSchema; rdb: MysqlConnections; table: Table): MysqlSchema
+
+ + + +
+
+
+
proc new(_: type MysqlSchema; rdb: MysqlConnections; table: Table;
+         column: Column): MysqlSchema
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.idx new file mode 100644 index 0000000..a6a6535 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/mysql_query_type.idx @@ -0,0 +1,5 @@ +nimTitle mysql_query_type schema_builder/queries/mysql/mysql_query_type.html module src/allographer/schema_builder/queries/mysql/mysql_query_type 0 +nim MysqlSchema schema_builder/queries/mysql/mysql_query_type.html#MysqlSchema type MysqlSchema 6 +nim new schema_builder/queries/mysql/mysql_query_type.html#new,typeMysqlSchema,MysqlConnections,Table proc new(_: type MysqlSchema; rdb: MysqlConnections; table: Table): MysqlSchema 11 +nim new schema_builder/queries/mysql/mysql_query_type.html#new,typeMysqlSchema,MysqlConnections,Table,Column proc new(_: type MysqlSchema; rdb: MysqlConnections; table: Table; column: Column): MysqlSchema 14 +nimgrp new schema_builder/queries/mysql/mysql_query_type.html#new-procs-all proc 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.html new file mode 100644 index 0000000..b8af5cd --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/rename_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/rename_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameColumn(self: MysqlSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.idx new file mode 100644 index 0000000..20d2cd2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_column.idx @@ -0,0 +1,2 @@ +nimTitle rename_column schema_builder/queries/mysql/rename_column.html module src/allographer/schema_builder/queries/mysql/rename_column 0 +nim renameColumn schema_builder/queries/mysql/rename_column.html#renameColumn,MysqlSchema,bool proc renameColumn(self: MysqlSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.html new file mode 100644 index 0000000..9ccdfd4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/rename_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/rename_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameTable(self: MysqlSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.idx new file mode 100644 index 0000000..84a4b81 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/rename_table.idx @@ -0,0 +1,2 @@ +nimTitle rename_table schema_builder/queries/mysql/rename_table.html module src/allographer/schema_builder/queries/mysql/rename_table 0 +nim renameTable schema_builder/queries/mysql/rename_table.html#renameTable,MysqlSchema,bool proc renameTable(self: MysqlSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.html new file mode 100644 index 0000000..6362af0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.html @@ -0,0 +1,135 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/reset_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/reset_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc resetMigrationTable(self: MysqlSchema) {....raises: [ValueError, Exception,
+    OSError, KeyError, IOError, DbError, CatchableError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc resetTable(self: MysqlSchema) {....raises: [ValueError, Exception, OSError,
+    IOError, DbError], tags: [TimeEffect, RootEffect, WriteDirEffect,
+                              ReadDirEffect, ReadIOEffect, WriteIOEffect],
+                                     forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.idx new file mode 100644 index 0000000..2b75dae --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/reset_table.idx @@ -0,0 +1,3 @@ +nimTitle reset_table schema_builder/queries/mysql/reset_table.html module src/allographer/schema_builder/queries/mysql/reset_table 0 +nim resetMigrationTable schema_builder/queries/mysql/reset_table.html#resetMigrationTable,MysqlSchema proc resetMigrationTable(self: MysqlSchema) 9 +nim resetTable schema_builder/queries/mysql/reset_table.html#resetTable,MysqlSchema proc resetTable(self: MysqlSchema) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.html new file mode 100644 index 0000000..10ac1c2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.html @@ -0,0 +1,202 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/schema_utils + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/schema_utils

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(rdb: MysqlConnections; queries: seq[string]) {.
+    ...raises: [ValueError, Exception, OSError, IOError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execThenSaveHistory(rdb: MysqlConnections; tableName: string;
+                         queries: seq[string]; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+
+
proc execThenSaveHistory(rdb: MysqlConnections; tableName: string;
+                         query: string; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc notAllowedOption(option, typ, column: string) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ + {option} is not allowed in {typ} column {column} + +
+
+ +
+
+
+
proc notAllowedType(typ: string) {....raises: [DbError], tags: [], forbids: [].}
+
+ + Change to {typ} type is not allowed + +
+
+ +
+
+
+
proc shouldRun(rdb: MysqlConnections; table: Table; checksum: string;
+               isReset: bool): bool {....raises: [ValueError, Exception, OSError,
+    KeyError, IOError, CatchableError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.idx new file mode 100644 index 0000000..7b1a129 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/schema_utils.idx @@ -0,0 +1,8 @@ +nimTitle schema_utils schema_builder/queries/mysql/schema_utils.html module src/allographer/schema_builder/queries/mysql/schema_utils 0 +nim notAllowedOption schema_builder/queries/mysql/schema_utils.html#notAllowedOption,string,string,string proc notAllowedOption(option, typ, column: string) 14 +nim notAllowedType schema_builder/queries/mysql/schema_utils.html#notAllowedType,string proc notAllowedType(typ: string) 18 +nim shouldRun schema_builder/queries/mysql/schema_utils.html#shouldRun,MysqlConnections,Table,string,bool proc shouldRun(rdb: MysqlConnections; table: Table; checksum: string; isReset: bool): bool 27 +nim exec schema_builder/queries/mysql/schema_utils.html#exec,MysqlConnections,seq[string] proc exec(rdb: MysqlConnections; queries: seq[string]) 38 +nim execThenSaveHistory schema_builder/queries/mysql/schema_utils.html#execThenSaveHistory,MysqlConnections,string,seq[string],string proc execThenSaveHistory(rdb: MysqlConnections; tableName: string;\n queries: seq[string]; checksum: string) 56 +nim execThenSaveHistory schema_builder/queries/mysql/schema_utils.html#execThenSaveHistory,MysqlConnections,string,string,string proc execThenSaveHistory(rdb: MysqlConnections; tableName: string; query: string;\n checksum: string) 85 +nimgrp execthensavehistory schema_builder/queries/mysql/schema_utils.html#execThenSaveHistory-procs-all proc 56 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.html new file mode 100644 index 0000000..04bdc60 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.html @@ -0,0 +1,115 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/sub/add_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/sub/add_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

In MySQL, BLOB, TEXT, GEOMETRY or JSON column can't have a default value.

+ +
+

Procs

+
+
+
+
proc addColumnString(table: Table; column: Column): seq[string] {.
+    ...raises: [KeyError, DbError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.idx new file mode 100644 index 0000000..30567a0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/add_column_query.idx @@ -0,0 +1,2 @@ +nimTitle add_column_query schema_builder/queries/mysql/sub/add_column_query.html module src/allographer/schema_builder/queries/mysql/sub/add_column_query 0 +nim addColumnString schema_builder/queries/mysql/sub/add_column_query.html#addColumnString,Table,Column proc addColumnString(table: Table; column: Column): seq[string] 478 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.html new file mode 100644 index 0000000..432092d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.html @@ -0,0 +1,147 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/sub/change_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/sub/change_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addIndexString(column: Column; table: Table): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc changeColumnString(table: Table; column: Column): seq[string] {.
+    ...raises: [DbError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc changeForeignKey(column: Column; table: Table): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.idx new file mode 100644 index 0000000..46d343d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/change_column_query.idx @@ -0,0 +1,4 @@ +nimTitle change_column_query schema_builder/queries/mysql/sub/change_column_query.html module src/allographer/schema_builder/queries/mysql/sub/change_column_query 0 +nim changeForeignKey schema_builder/queries/mysql/sub/change_column_query.html#changeForeignKey,Column,Table proc changeForeignKey(column: Column; table: Table): string 455 +nim addIndexString schema_builder/queries/mysql/sub/change_column_query.html#addIndexString,Column,Table proc addIndexString(column: Column; table: Table): string 472 +nim changeColumnString schema_builder/queries/mysql/sub/change_column_query.html#changeColumnString,Table,Column proc changeColumnString(table: Table; column: Column): seq[string] 476 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.html new file mode 100644 index 0000000..c1164ed --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.html @@ -0,0 +1,163 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/sub/create_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/sub/create_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

In MySQL, BLOB, TEXT, GEOMETRY or JSON column can't have a default value. BLOB/TEXT column caughn't use in key specification without a key length.

+ +
+

Procs

+
+
+
+
proc alterAddForeignKey(column: Column; table: Table): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createColumnString(table: Table; column: Column): string {.
+    ...raises: [KeyError, DbError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createForeignKey(table: Table; column: Column): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createIndexString(table: Table; column: Column): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.idx new file mode 100644 index 0000000..fd8f326 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/create_column_query.idx @@ -0,0 +1,5 @@ +nimTitle create_column_query schema_builder/queries/mysql/sub/create_column_query.html module src/allographer/schema_builder/queries/mysql/sub/create_column_query 0 +nim createForeignKey schema_builder/queries/mysql/sub/create_column_query.html#createForeignKey,Table,Column proc createForeignKey(table: Table; column: Column): string 502 +nim alterAddForeignKey schema_builder/queries/mysql/sub/create_column_query.html#alterAddForeignKey,Column,Table proc alterAddForeignKey(column: Column; table: Table): string 518 +nim createIndexString schema_builder/queries/mysql/sub/create_column_query.html#createIndexString,Table,Column proc createIndexString(table: Table; column: Column): string 545 +nim createColumnString schema_builder/queries/mysql/sub/create_column_query.html#createColumnString,Table,Column proc createColumnString(table: Table; column: Column): string 549 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.html b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.html new file mode 100644 index 0000000..c9b0d3c --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.html @@ -0,0 +1,137 @@ + + + + + + + +src/allographer/schema_builder/queries/mysql/sub/is_exists + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/mysql/sub/is_exists

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc isExistsIndex(rdb: MysqlConnections; table: Table; column: Column): Future[
+    bool] {....stackTrace: false, raises: [Exception, ValueError, OSError, IOError,
+                                        DbError, JsonParsingError, KeyError], tags: [
+    RootEffect, TimeEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc isExistsUnique(rdb: MysqlConnections; table: Table; column: Column): Future[
+    bool] {....stackTrace: false, raises: [Exception, ValueError, OSError, IOError,
+                                        DbError, JsonParsingError, KeyError], tags: [
+    RootEffect, TimeEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.idx new file mode 100644 index 0000000..4881981 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/mysql/sub/is_exists.idx @@ -0,0 +1,3 @@ +nimTitle is_exists schema_builder/queries/mysql/sub/is_exists.html module src/allographer/schema_builder/queries/mysql/sub/is_exists 0 +nim isExistsIndex schema_builder/queries/mysql/sub/is_exists.html#isExistsIndex,MysqlConnections,Table,Column proc isExistsIndex(rdb: MysqlConnections; table: Table; column: Column): Future[bool] 12 +nim isExistsUnique schema_builder/queries/mysql/sub/is_exists.html#isExistsUnique,MysqlConnections,Table,Column proc isExistsUnique(rdb: MysqlConnections; table: Table; column: Column): Future[bool] 24 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.html new file mode 100644 index 0000000..49d78ed --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/add_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/add_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addColumn(self: PostgresSchema; isReset: bool) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.idx new file mode 100644 index 0000000..6bd8583 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/add_column.idx @@ -0,0 +1,2 @@ +nimTitle add_column schema_builder/queries/postgres/add_column.html module src/allographer/schema_builder/queries/postgres/add_column 0 +nim addColumn schema_builder/queries/postgres/add_column.html#addColumn,PostgresSchema,bool proc addColumn(self: PostgresSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.html new file mode 100644 index 0000000..7ec207b --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/change_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/change_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc changeColumn(self: PostgresSchema; isReset: bool) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.idx new file mode 100644 index 0000000..7bb8485 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/change_column.idx @@ -0,0 +1,2 @@ +nimTitle change_column schema_builder/queries/postgres/change_column.html module src/allographer/schema_builder/queries/postgres/change_column 0 +nim changeColumn schema_builder/queries/postgres/change_column.html#changeColumn,PostgresSchema,bool proc changeColumn(self: PostgresSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.html new file mode 100644 index 0000000..f01e537 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/create_migration_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/create_migration_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createMigrationTable(self: PostgresSchema) {.
+    ...raises: [DbError, KeyError, ValueError, Exception, OSError, IOError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.idx new file mode 100644 index 0000000..83c7217 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_migration_table.idx @@ -0,0 +1,2 @@ +nimTitle create_migration_table schema_builder/queries/postgres/create_migration_table.html module src/allographer/schema_builder/queries/postgres/create_migration_table 0 +nim createMigrationTable schema_builder/queries/postgres/create_migration_table.html#createMigrationTable,PostgresSchema proc createMigrationTable(self: PostgresSchema) 10 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.html new file mode 100644 index 0000000..aafa94b --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/create_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/create_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createTable(self: PostgresSchema; isReset: bool) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.idx new file mode 100644 index 0000000..da0b6af --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/create_table.idx @@ -0,0 +1,2 @@ +nimTitle create_table schema_builder/queries/postgres/create_table.html module src/allographer/schema_builder/queries/postgres/create_table 0 +nim createTable schema_builder/queries/postgres/create_table.html#createTable,PostgresSchema,bool proc createTable(self: PostgresSchema; isReset: bool) 18 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.html new file mode 100644 index 0000000..7d5547a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/drop_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/drop_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropColumn(self: PostgresSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.idx new file mode 100644 index 0000000..ed0d6ea --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_column.idx @@ -0,0 +1,2 @@ +nimTitle drop_column schema_builder/queries/postgres/drop_column.html module src/allographer/schema_builder/queries/postgres/drop_column 0 +nim dropColumn schema_builder/queries/postgres/drop_column.html#dropColumn,PostgresSchema,bool proc dropColumn(self: PostgresSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.html new file mode 100644 index 0000000..9b2458f --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/drop_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/drop_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropTable(self: PostgresSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.idx new file mode 100644 index 0000000..07cf021 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/drop_table.idx @@ -0,0 +1,2 @@ +nimTitle drop_table schema_builder/queries/postgres/drop_table.html module src/allographer/schema_builder/queries/postgres/drop_table 0 +nim dropTable schema_builder/queries/postgres/drop_table.html#dropTable,PostgresSchema,bool proc dropTable(self: PostgresSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.html new file mode 100644 index 0000000..8e90f42 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.html @@ -0,0 +1,155 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/postgres_query_type + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/postgres_query_type

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
PostgresSchema = ref object
+  rdb*: PostgresConnections
+  table*: Table
+  column*: Column
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc new(_: type PostgresSchema; rdb: PostgresConnections; table: Table): PostgresSchema
+
+ + + +
+
+
+
proc new(_: type PostgresSchema; rdb: PostgresConnections; table: Table;
+         column: Column): PostgresSchema
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.idx new file mode 100644 index 0000000..dd90cbe --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/postgres_query_type.idx @@ -0,0 +1,5 @@ +nimTitle postgres_query_type schema_builder/queries/postgres/postgres_query_type.html module src/allographer/schema_builder/queries/postgres/postgres_query_type 0 +nim PostgresSchema schema_builder/queries/postgres/postgres_query_type.html#PostgresSchema type PostgresSchema 6 +nim new schema_builder/queries/postgres/postgres_query_type.html#new,typePostgresSchema,PostgresConnections,Table proc new(_: type PostgresSchema; rdb: PostgresConnections; table: Table): PostgresSchema 11 +nim new schema_builder/queries/postgres/postgres_query_type.html#new,typePostgresSchema,PostgresConnections,Table,Column proc new(_: type PostgresSchema; rdb: PostgresConnections; table: Table;\n column: Column): PostgresSchema 14 +nimgrp new schema_builder/queries/postgres/postgres_query_type.html#new-procs-all proc 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.html new file mode 100644 index 0000000..6d11c46 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/rename_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/rename_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameColumn(self: PostgresSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.idx new file mode 100644 index 0000000..83cc905 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_column.idx @@ -0,0 +1,2 @@ +nimTitle rename_column schema_builder/queries/postgres/rename_column.html module src/allographer/schema_builder/queries/postgres/rename_column 0 +nim renameColumn schema_builder/queries/postgres/rename_column.html#renameColumn,PostgresSchema,bool proc renameColumn(self: PostgresSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.html new file mode 100644 index 0000000..549feb5 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/rename_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/rename_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameTable(self: PostgresSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.idx new file mode 100644 index 0000000..3b9349d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/rename_table.idx @@ -0,0 +1,2 @@ +nimTitle rename_table schema_builder/queries/postgres/rename_table.html module src/allographer/schema_builder/queries/postgres/rename_table 0 +nim renameTable schema_builder/queries/postgres/rename_table.html#renameTable,PostgresSchema,bool proc renameTable(self: PostgresSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.html new file mode 100644 index 0000000..636b7e1 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.html @@ -0,0 +1,135 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/reset_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/reset_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc resetMigrationTable(self: PostgresSchema) {....raises: [ValueError, Exception,
+    OSError, KeyError, IOError, DbError, CatchableError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc resetTable(self: PostgresSchema) {....raises: [ValueError, Exception, OSError,
+    IOError, DbError], tags: [TimeEffect, RootEffect, WriteDirEffect,
+                              ReadDirEffect, ReadIOEffect, WriteIOEffect],
+                                        forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.idx new file mode 100644 index 0000000..7cc3d65 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/reset_table.idx @@ -0,0 +1,3 @@ +nimTitle reset_table schema_builder/queries/postgres/reset_table.html module src/allographer/schema_builder/queries/postgres/reset_table 0 +nim resetMigrationTable schema_builder/queries/postgres/reset_table.html#resetMigrationTable,PostgresSchema proc resetMigrationTable(self: PostgresSchema) 9 +nim resetTable schema_builder/queries/postgres/reset_table.html#resetTable,PostgresSchema proc resetTable(self: PostgresSchema) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.html new file mode 100644 index 0000000..3d03d4a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.html @@ -0,0 +1,204 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/schema_utils + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/schema_utils

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(rdb: PostgresConnections; queries: seq[string]) {.
+    ...raises: [ValueError, Exception, OSError, IOError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execThenSaveHistory(rdb: PostgresConnections; tableName: string;
+                         queries: seq[string]; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+
+
proc execThenSaveHistory(rdb: PostgresConnections; tableName: string;
+                         query: string; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc notAllowedOption(option, typ, column: string) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ + {option} is not allowed in {typ} column {column} + +
+
+ +
+
+
+
proc notAllowedType(typ: string) {....raises: [DbError], tags: [], forbids: [].}
+
+ + Change to {typ} type is not allowed + +
+
+ +
+
+
+
proc shouldRun(rdb: PostgresConnections; table: Table; checksum: string;
+               isReset: bool): bool {....raises: [ValueError, Exception, OSError,
+    KeyError, IOError, CatchableError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.idx new file mode 100644 index 0000000..d971a09 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/schema_utils.idx @@ -0,0 +1,8 @@ +nimTitle schema_utils schema_builder/queries/postgres/schema_utils.html module src/allographer/schema_builder/queries/postgres/schema_utils 0 +nim notAllowedOption schema_builder/queries/postgres/schema_utils.html#notAllowedOption,string,string,string proc notAllowedOption(option, typ, column: string) 14 +nim notAllowedType schema_builder/queries/postgres/schema_utils.html#notAllowedType,string proc notAllowedType(typ: string) 18 +nim shouldRun schema_builder/queries/postgres/schema_utils.html#shouldRun,PostgresConnections,Table,string,bool proc shouldRun(rdb: PostgresConnections; table: Table; checksum: string;\n isReset: bool): bool 27 +nim exec schema_builder/queries/postgres/schema_utils.html#exec,PostgresConnections,seq[string] proc exec(rdb: PostgresConnections; queries: seq[string]) 38 +nim execThenSaveHistory schema_builder/queries/postgres/schema_utils.html#execThenSaveHistory,PostgresConnections,string,seq[string],string proc execThenSaveHistory(rdb: PostgresConnections; tableName: string;\n queries: seq[string]; checksum: string) 56 +nim execThenSaveHistory schema_builder/queries/postgres/schema_utils.html#execThenSaveHistory,PostgresConnections,string,string,string proc execThenSaveHistory(rdb: PostgresConnections; tableName: string; query: string;\n checksum: string) 85 +nimgrp execthensavehistory schema_builder/queries/postgres/schema_utils.html#execThenSaveHistory-procs-all proc 56 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.html new file mode 100644 index 0000000..157dcef --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.html @@ -0,0 +1,115 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/sub/add_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/sub/add_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addColumnString(table: Table; column: Column): seq[string] {.
+    ...raises: [KeyError, DbError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.idx new file mode 100644 index 0000000..aa3b1a7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/add_column_query.idx @@ -0,0 +1,2 @@ +nimTitle add_column_query schema_builder/queries/postgres/sub/add_column_query.html module src/allographer/schema_builder/queries/postgres/sub/add_column_query 0 +nim addColumnString schema_builder/queries/postgres/sub/add_column_query.html#addColumnString,Table,Column proc addColumnString(table: Table; column: Column): seq[string] 432 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.html new file mode 100644 index 0000000..a5c1aad --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.html @@ -0,0 +1,115 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/sub/change_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/sub/change_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc changeColumnString(table: Table; column: Column): seq[string] {.
+    ...raises: [KeyError, DbError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.idx new file mode 100644 index 0000000..28b007a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/change_column_query.idx @@ -0,0 +1,2 @@ +nimTitle change_column_query schema_builder/queries/postgres/sub/change_column_query.html module src/allographer/schema_builder/queries/postgres/sub/change_column_query 0 +nim changeColumnString schema_builder/queries/postgres/sub/change_column_query.html#changeColumnString,Table,Column proc changeColumnString(table: Table; column: Column): seq[string] 358 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.html new file mode 100644 index 0000000..ef1db23 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.html @@ -0,0 +1,179 @@ + + + + + + + +src/allographer/schema_builder/queries/postgres/sub/create_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/postgres/sub/create_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createColumnString(table: Table; column: Column): string {.
+    ...raises: [DbError, KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createCommentColumn(table: Table; column: Column): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createForeignString(table: Table; column: Column): string {.
+    ...raises: [KeyError], tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createIndexString(table: Table; column: Column): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createUpdatedAtString(table: Table; column: Column): seq[string] {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.idx new file mode 100644 index 0000000..c730645 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/postgres/sub/create_column_query.idx @@ -0,0 +1,6 @@ +nimTitle create_column_query schema_builder/queries/postgres/sub/create_column_query.html module src/allographer/schema_builder/queries/postgres/sub/create_column_query 0 +nim createColumnString schema_builder/queries/postgres/sub/create_column_query.html#createColumnString,Table,Column proc createColumnString(table: Table; column: Column): string 390 +nim createForeignString schema_builder/queries/postgres/sub/create_column_query.html#createForeignString,Table,Column proc createForeignString(table: Table; column: Column): string 453 +nim createIndexString schema_builder/queries/postgres/sub/create_column_query.html#createIndexString,Table,Column proc createIndexString(table: Table; column: Column): string 457 +nim createUpdatedAtString schema_builder/queries/postgres/sub/create_column_query.html#createUpdatedAtString,Table,Column proc createUpdatedAtString(table: Table; column: Column): seq[string] 461 +nim createCommentColumn schema_builder/queries/postgres/sub/create_column_query.html#createCommentColumn,Table,Column proc createCommentColumn(table: Table; column: Column): string 465 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.html new file mode 100644 index 0000000..a224ba0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/add_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/add_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addColumn(self: SqliteSchema; isReset: bool) {....raises: [DbError, KeyError,
+    ValueError, Exception, OSError, IOError, RegexError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.idx new file mode 100644 index 0000000..3759fc7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/add_column.idx @@ -0,0 +1,2 @@ +nimTitle add_column schema_builder/queries/sqlite/add_column.html module src/allographer/schema_builder/queries/sqlite/add_column 0 +nim addColumn schema_builder/queries/sqlite/add_column.html#addColumn,SqliteSchema,bool proc addColumn(self: SqliteSchema; isReset: bool) 44 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.html new file mode 100644 index 0000000..410b300 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.html @@ -0,0 +1,123 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/change_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/change_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc changeColumn(self: SqliteSchema; isReset: bool) {....raises: [DbError,
+    ValueError, Exception, OSError, IOError, KeyError, RegexError,
+    CatchableError], tags: [TimeEffect, RootEffect, WriteDirEffect,
+                            ReadDirEffect, ReadIOEffect, WriteIOEffect,
+                            ReadDbEffect], forbids: [].}
+
+ +
  • create tmp table with new column difinition
  • +
  • copy data from old table to tmp table
  • +
  • delete old table
  • +
  • rename tmp table name to old table name
  • +
+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.idx new file mode 100644 index 0000000..af280e8 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/change_column.idx @@ -0,0 +1,2 @@ +nimTitle change_column schema_builder/queries/sqlite/change_column.html module src/allographer/schema_builder/queries/sqlite/change_column 0 +nim changeColumn schema_builder/queries/sqlite/change_column.html#changeColumn,SqliteSchema,bool proc changeColumn(self: SqliteSchema; isReset: bool) 20 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.html new file mode 100644 index 0000000..15e8da1 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/create_migration_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/create_migration_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createMigrationTable(self: SqliteSchema) {.
+    ...raises: [DbError, KeyError, ValueError, Exception, OSError, IOError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.idx new file mode 100644 index 0000000..957d0c4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_migration_table.idx @@ -0,0 +1,2 @@ +nimTitle create_migration_table schema_builder/queries/sqlite/create_migration_table.html module src/allographer/schema_builder/queries/sqlite/create_migration_table 0 +nim createMigrationTable schema_builder/queries/sqlite/create_migration_table.html#createMigrationTable,SqliteSchema proc createMigrationTable(self: SqliteSchema) 10 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.html new file mode 100644 index 0000000..0926a7d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/create_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/create_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createTable(self: SqliteSchema; isReset: bool) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.idx new file mode 100644 index 0000000..0b2c63f --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/create_table.idx @@ -0,0 +1,2 @@ +nimTitle create_table schema_builder/queries/sqlite/create_table.html module src/allographer/schema_builder/queries/sqlite/create_table 0 +nim createTable schema_builder/queries/sqlite/create_table.html#createTable,SqliteSchema,bool proc createTable(self: SqliteSchema; isReset: bool) 18 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.html new file mode 100644 index 0000000..3d7be19 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.html @@ -0,0 +1,122 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/drop_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/drop_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropColumn(self: SqliteSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, IOError, DbError, KeyError, RegexError, CatchableError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ +
  • create tmp table with new column difinition
  • +
  • copy data from old table to tmp table
  • +
  • delete old table
  • +
  • rename tmp table name to old table name
  • +
+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.idx new file mode 100644 index 0000000..e62cba2 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_column.idx @@ -0,0 +1,2 @@ +nimTitle drop_column schema_builder/queries/sqlite/drop_column.html module src/allographer/schema_builder/queries/sqlite/drop_column 0 +nim dropColumn schema_builder/queries/sqlite/drop_column.html#dropColumn,SqliteSchema,bool proc dropColumn(self: SqliteSchema; isReset: bool) 20 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.html new file mode 100644 index 0000000..b490897 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/drop_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/drop_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropTable(self: SqliteSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.idx new file mode 100644 index 0000000..ad00794 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/drop_table.idx @@ -0,0 +1,2 @@ +nimTitle drop_table schema_builder/queries/sqlite/drop_table.html module src/allographer/schema_builder/queries/sqlite/drop_table 0 +nim dropTable schema_builder/queries/sqlite/drop_table.html#dropTable,SqliteSchema,bool proc dropTable(self: SqliteSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.html new file mode 100644 index 0000000..e339bf3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/rename_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/rename_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameColumn(self: SqliteSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.idx new file mode 100644 index 0000000..4640a93 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_column.idx @@ -0,0 +1,2 @@ +nimTitle rename_column schema_builder/queries/sqlite/rename_column.html module src/allographer/schema_builder/queries/sqlite/rename_column 0 +nim renameColumn schema_builder/queries/sqlite/rename_column.html#renameColumn,SqliteSchema,bool proc renameColumn(self: SqliteSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.html new file mode 100644 index 0000000..10616a3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/rename_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/rename_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameTable(self: SqliteSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.idx new file mode 100644 index 0000000..02b3ea6 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/rename_table.idx @@ -0,0 +1,2 @@ +nimTitle rename_table schema_builder/queries/sqlite/rename_table.html module src/allographer/schema_builder/queries/sqlite/rename_table 0 +nim renameTable schema_builder/queries/sqlite/rename_table.html#renameTable,SqliteSchema,bool proc renameTable(self: SqliteSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.html new file mode 100644 index 0000000..4dab694 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.html @@ -0,0 +1,135 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/reset_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/reset_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc resetMigrationTable(self: SqliteSchema) {....raises: [ValueError, Exception,
+    OSError, KeyError, IOError, DbError, CatchableError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect,
+    ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc resetTable(self: SqliteSchema) {....raises: [ValueError, Exception, OSError,
+    IOError, DbError], tags: [TimeEffect, RootEffect, WriteDirEffect,
+                              ReadDirEffect, ReadIOEffect, WriteIOEffect],
+                                      forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.idx new file mode 100644 index 0000000..bcea291 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/reset_table.idx @@ -0,0 +1,3 @@ +nimTitle reset_table schema_builder/queries/sqlite/reset_table.html module src/allographer/schema_builder/queries/sqlite/reset_table 0 +nim resetMigrationTable schema_builder/queries/sqlite/reset_table.html#resetMigrationTable,SqliteSchema proc resetMigrationTable(self: SqliteSchema) 9 +nim resetTable schema_builder/queries/sqlite/reset_table.html#resetTable,SqliteSchema proc resetTable(self: SqliteSchema) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.html new file mode 100644 index 0000000..2dab71e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.html @@ -0,0 +1,219 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/schema_utils + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/schema_utils

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(rdb: SqliteConnections; queries: seq[string]) {.
+    ...raises: [ValueError, Exception, OSError, IOError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execThenSaveHistory(rdb: SqliteConnections; tableName: string;
+                         queries: seq[string]; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+
+
proc execThenSaveHistory(rdb: SqliteConnections; tableName: string;
+                         query: string; checksum: string) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc notAllowedOption(option, typ, column: string) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ + {option} is not allowed in {typ} column {column} + +
+
+ +
+
+
+
proc notAllowedType(typ: string) {....raises: [DbError], tags: [], forbids: [].}
+
+ + Change to {typ} type is not allowed + +
+
+ +
+
+
+
proc shouldRun(rdb: SqliteConnections; table: Table; checksum: string;
+               isReset: bool): bool {....raises: [ValueError, Exception, OSError,
+    KeyError, IOError, CatchableError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, ReadDbEffect],
+                                      forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc sqliteLengthCheck(column: Column; maxLength: int): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + "CHECK (length({column.name}) <= {maxLength})" + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.idx new file mode 100644 index 0000000..e7be6f3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/schema_utils.idx @@ -0,0 +1,9 @@ +nimTitle schema_utils schema_builder/queries/sqlite/schema_utils.html module src/allographer/schema_builder/queries/sqlite/schema_utils 0 +nim notAllowedOption schema_builder/queries/sqlite/schema_utils.html#notAllowedOption,string,string,string proc notAllowedOption(option, typ, column: string) 15 +nim notAllowedType schema_builder/queries/sqlite/schema_utils.html#notAllowedType,string proc notAllowedType(typ: string) 19 +nim sqliteLengthCheck schema_builder/queries/sqlite/schema_utils.html#sqliteLengthCheck,Column,int proc sqliteLengthCheck(column: Column; maxLength: int): string 24 +nim shouldRun schema_builder/queries/sqlite/schema_utils.html#shouldRun,SqliteConnections,Table,string,bool proc shouldRun(rdb: SqliteConnections; table: Table; checksum: string; isReset: bool): bool 34 +nim exec schema_builder/queries/sqlite/schema_utils.html#exec,SqliteConnections,seq[string] proc exec(rdb: SqliteConnections; queries: seq[string]) 45 +nim execThenSaveHistory schema_builder/queries/sqlite/schema_utils.html#execThenSaveHistory,SqliteConnections,string,seq[string],string proc execThenSaveHistory(rdb: SqliteConnections; tableName: string;\n queries: seq[string]; checksum: string) 63 +nim execThenSaveHistory schema_builder/queries/sqlite/schema_utils.html#execThenSaveHistory,SqliteConnections,string,string,string proc execThenSaveHistory(rdb: SqliteConnections; tableName: string; query: string;\n checksum: string) 92 +nimgrp execthensavehistory schema_builder/queries/sqlite/schema_utils.html#execThenSaveHistory-procs-all proc 63 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.html new file mode 100644 index 0000000..a560eb5 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.html @@ -0,0 +1,153 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/sqlite_query_type + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/sqlite_query_type

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
SqliteSchema = ref object
+  rdb*: SqliteConnections
+  table*: Table
+  column*: Column
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc new(_: type SqliteSchema; rdb: SqliteConnections; table: Table): SqliteSchema
+
+ + + +
+
+
+
proc new(_: type SqliteSchema; rdb: SqliteConnections; table: Table;
+         column: Column): SqliteSchema
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.idx new file mode 100644 index 0000000..a888f16 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sqlite_query_type.idx @@ -0,0 +1,5 @@ +nimTitle sqlite_query_type schema_builder/queries/sqlite/sqlite_query_type.html module src/allographer/schema_builder/queries/sqlite/sqlite_query_type 0 +nim SqliteSchema schema_builder/queries/sqlite/sqlite_query_type.html#SqliteSchema type SqliteSchema 5 +nim new schema_builder/queries/sqlite/sqlite_query_type.html#new,typeSqliteSchema,SqliteConnections,Table proc new(_: type SqliteSchema; rdb: SqliteConnections; table: Table): SqliteSchema 10 +nim new schema_builder/queries/sqlite/sqlite_query_type.html#new,typeSqliteSchema,SqliteConnections,Table,Column proc new(_: type SqliteSchema; rdb: SqliteConnections; table: Table; column: Column): SqliteSchema 13 +nimgrp new schema_builder/queries/sqlite/sqlite_query_type.html#new-procs-all proc 10 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.html new file mode 100644 index 0000000..31cc3cd --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.html @@ -0,0 +1,120 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/sub/add_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/sub/add_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

https://www.sqlite.org/lang_createtable.html#constraints

+ +
+

Procs

+
+
+
+
proc addColumnString(rdb: SqliteConnections; table: Table; column: Column): seq[
+    string] {....raises: [DbError, KeyError, ValueError, Exception, OSError,
+                       IOError, RegexError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, ReadDbEffect],
+              forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.idx new file mode 100644 index 0000000..4bfcf78 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/add_column_query.idx @@ -0,0 +1,2 @@ +nimTitle add_column_query schema_builder/queries/sqlite/sub/add_column_query.html module src/allographer/schema_builder/queries/sqlite/sub/add_column_query 0 +nim addColumnString schema_builder/queries/sqlite/sub/add_column_query.html#addColumnString,SqliteConnections,Table,Column proc addColumnString(rdb: SqliteConnections; table: Table; column: Column): seq[\n string] 363 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.html new file mode 100644 index 0000000..0e5c7a1 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.html @@ -0,0 +1,147 @@ + + + + + + + +src/allographer/schema_builder/queries/sqlite/sub/create_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/sqlite/sub/create_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

https://www.sqlite.org/lang_createtable.html#constraints

+ +
+

Procs

+
+
+
+
proc createColumnString(column: Column): string {....raises: [DbError, KeyError],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createForeignString(column: Column): string {....raises: [KeyError], tags: [],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc createIndexString(table: Table; column: Column): string {....raises: [],
+    tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.idx new file mode 100644 index 0000000..3d7ee26 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/sqlite/sub/create_column_query.idx @@ -0,0 +1,4 @@ +nimTitle create_column_query schema_builder/queries/sqlite/sub/create_column_query.html module src/allographer/schema_builder/queries/sqlite/sub/create_column_query 0 +nim createColumnString schema_builder/queries/sqlite/sub/create_column_query.html#createColumnString,Column proc createColumnString(column: Column): string 339 +nim createForeignString schema_builder/queries/sqlite/sub/create_column_query.html#createForeignString,Column proc createForeignString(column: Column): string 402 +nim createIndexString schema_builder/queries/sqlite/sub/create_column_query.html#createIndexString,Table,Column proc createIndexString(table: Table; column: Column): string 407 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.html new file mode 100644 index 0000000..0e123ad --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/add_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/add_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc addColumn(self: SurrealSchema; isReset: bool) {....raises: [DbError, KeyError,
+    ValueError, Exception, OSError, IOError, CatchableError, HttpRequestError,
+    LibraryError, SslError, ProtocolError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.idx new file mode 100644 index 0000000..78d58a7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/add_column.idx @@ -0,0 +1,2 @@ +nimTitle add_column schema_builder/queries/surreal/add_column.html module src/allographer/schema_builder/queries/surreal/add_column 0 +nim addColumn schema_builder/queries/surreal/add_column.html#addColumn,SurrealSchema,bool proc addColumn(self: SurrealSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.html new file mode 100644 index 0000000..180c072 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/change_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/change_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc changeColumn(self: SurrealSchema; isReset: bool) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ +

SurrealDB not support change column field

+

https://github.com/surrealdb/surrealdb/issues/1838

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.idx new file mode 100644 index 0000000..528ce7c --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/change_column.idx @@ -0,0 +1,2 @@ +nimTitle change_column schema_builder/queries/surreal/change_column.html module src/allographer/schema_builder/queries/surreal/change_column 0 +nim changeColumn schema_builder/queries/surreal/change_column.html#changeColumn,SurrealSchema,bool proc changeColumn(self: SurrealSchema; isReset: bool) 17 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.html new file mode 100644 index 0000000..2f4f25e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/create_migration_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/create_migration_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

https://surrealdb.com/docs/surrealql/statements/define/table

+ +
+

Procs

+
+
+
+
proc createMigrationTable(self: SurrealSchema; isReset: bool = false) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError, DbError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.idx new file mode 100644 index 0000000..5deb6a0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_migration_table.idx @@ -0,0 +1,2 @@ +nimTitle create_migration_table schema_builder/queries/surreal/create_migration_table.html module src/allographer/schema_builder/queries/surreal/create_migration_table 0 +nim createMigrationTable schema_builder/queries/surreal/create_migration_table.html#createMigrationTable,SurrealSchema,bool proc createMigrationTable(self: SurrealSchema; isReset: bool = false) 16 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.html new file mode 100644 index 0000000..9617c21 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.html @@ -0,0 +1,136 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/create_sequence_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/create_sequence_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSequenceTable(rdb: SurrealConnections) {.
+    ...raises: [ValueError, Exception, OSError, IOError, KeyError], tags: [
+    TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect,
+    WriteIOEffect], forbids: [].}
+
+ + if _autoincrement_sequences table is not exists, create it + +
+
+ +
+
+
+
proc resetSequence(rdb: SurrealConnections; table: Table) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, DbError, HttpRequestError,
+    LibraryError, SslError, ProtocolError, CatchableError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.idx new file mode 100644 index 0000000..80209ee --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_sequence_table.idx @@ -0,0 +1,3 @@ +nimTitle create_sequence_table schema_builder/queries/surreal/create_sequence_table.html module src/allographer/schema_builder/queries/surreal/create_sequence_table 0 +nim createSequenceTable schema_builder/queries/surreal/create_sequence_table.html#createSequenceTable,SurrealConnections proc createSequenceTable(rdb: SurrealConnections) 10 +nim resetSequence schema_builder/queries/surreal/create_sequence_table.html#resetSequence,SurrealConnections,Table proc resetSequence(rdb: SurrealConnections; table: Table) 31 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.html new file mode 100644 index 0000000..5be97de --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/create_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/create_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

https://surrealdb.com/docs/surrealql/statements/define/table

+ +
+

Procs

+
+
+
+
proc createTable(self: SurrealSchema; isReset: bool) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.idx new file mode 100644 index 0000000..0182b50 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/create_table.idx @@ -0,0 +1,2 @@ +nimTitle create_table schema_builder/queries/surreal/create_table.html module src/allographer/schema_builder/queries/surreal/create_table 0 +nim createTable schema_builder/queries/surreal/create_table.html#createTable,SurrealSchema,bool proc createTable(self: SurrealSchema; isReset: bool) 17 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.html new file mode 100644 index 0000000..878f7ce --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/drop_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/drop_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropColumn(self: SurrealSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.idx new file mode 100644 index 0000000..77f73d4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_column.idx @@ -0,0 +1,2 @@ +nimTitle drop_column schema_builder/queries/surreal/drop_column.html module src/allographer/schema_builder/queries/surreal/drop_column 0 +nim dropColumn schema_builder/queries/surreal/drop_column.html#dropColumn,SurrealSchema,bool proc dropColumn(self: SurrealSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.html new file mode 100644 index 0000000..921ff9e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/drop_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/drop_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc dropTable(self: SurrealSchema; isReset: bool) {....raises: [ValueError,
+    Exception, OSError, KeyError, IOError, CatchableError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.idx new file mode 100644 index 0000000..4775bdc --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/drop_table.idx @@ -0,0 +1,2 @@ +nimTitle drop_table schema_builder/queries/surreal/drop_table.html module src/allographer/schema_builder/queries/surreal/drop_table 0 +nim dropTable schema_builder/queries/surreal/drop_table.html#dropTable,SurrealSchema,bool proc dropTable(self: SurrealSchema; isReset: bool) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.html new file mode 100644 index 0000000..b8febe4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/rename_column + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/rename_column

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameColumn(self: SurrealSchema; isReset: bool) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ +

SurrealDB not support rename column field

+

https://github.com/surrealdb/surrealdb/issues/1615

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.idx new file mode 100644 index 0000000..20e028f --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_column.idx @@ -0,0 +1,2 @@ +nimTitle rename_column schema_builder/queries/surreal/rename_column.html module src/allographer/schema_builder/queries/surreal/rename_column 0 +nim renameColumn schema_builder/queries/surreal/rename_column.html#renameColumn,SurrealSchema,bool proc renameColumn(self: SurrealSchema; isReset: bool) 15 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.html new file mode 100644 index 0000000..babb196 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/rename_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/rename_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc renameTable(self: SurrealSchema; isReset: bool) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ +

SurrealDB not support rename table field

+

https://github.com/surrealdb/surrealdb/issues/1615

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.idx new file mode 100644 index 0000000..d7a2f9e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/rename_table.idx @@ -0,0 +1,2 @@ +nimTitle rename_table schema_builder/queries/surreal/rename_table.html module src/allographer/schema_builder/queries/surreal/rename_table 0 +nim renameTable schema_builder/queries/surreal/rename_table.html#renameTable,SurrealSchema,bool proc renameTable(self: SurrealSchema; isReset: bool) 14 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.html new file mode 100644 index 0000000..bf840cd --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.html @@ -0,0 +1,134 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/reset_table + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/reset_table

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc resetMigrationTable(self: SurrealSchema) {....raises: [ValueError, Exception,
+    OSError, KeyError, IOError, DbError, HttpRequestError, LibraryError,
+    SslError, ProtocolError, CatchableError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc resetTable(self: SurrealSchema) {....raises: [ValueError, Exception, OSError,
+    IOError], tags: [TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+                     ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.idx new file mode 100644 index 0000000..829b2f5 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/reset_table.idx @@ -0,0 +1,3 @@ +nimTitle reset_table schema_builder/queries/surreal/reset_table.html module src/allographer/schema_builder/queries/surreal/reset_table 0 +nim resetMigrationTable schema_builder/queries/surreal/reset_table.html#resetMigrationTable,SurrealSchema proc resetMigrationTable(self: SurrealSchema) 9 +nim resetTable schema_builder/queries/surreal/reset_table.html#resetTable,SurrealSchema proc resetTable(self: SurrealSchema) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.html new file mode 100644 index 0000000..94de6f8 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.html @@ -0,0 +1,203 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/schema_utils + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/schema_utils

+
+ +
+ +
+ +

+ +
+

Procs

+
+
+
+
proc exec(rdb: SurrealConnections; queries: seq[string]) {.
+    ...raises: [ValueError, Exception, OSError, IOError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc execThenSaveHistory(rdb: SurrealConnections; tableName: string;
+                         queries: seq[string]; checksum: string) {....raises: [
+    ValueError, Exception, OSError, IOError, KeyError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [TimeEffect,
+    RootEffect, WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+
+
proc execThenSaveHistory(rdb: SurrealConnections; tableName: string;
+                         query: string; checksum: string) {....raises: [ValueError,
+    Exception, OSError, IOError, KeyError, DbError, HttpRequestError,
+    LibraryError, SslError, ProtocolError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+
+
+
proc notAllowedOption(option, typ, column: string) {....raises: [DbError],
+    tags: [], forbids: [].}
+
+ + {option} is not allowed in {typ} column {column} + +
+
+ +
+
+
+
proc notAllowedType(typ: string) {....raises: [DbError], tags: [], forbids: [].}
+
+ + Change to {typ} type is not allowed + +
+
+ +
+
+
+
proc shouldRun(rdb: SurrealConnections; table: Table; checksum: string;
+               isReset: bool): bool {....raises: [ValueError, Exception, OSError,
+    KeyError, IOError, CatchableError], tags: [TimeEffect, RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.idx new file mode 100644 index 0000000..2f8656d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/schema_utils.idx @@ -0,0 +1,8 @@ +nimTitle schema_utils schema_builder/queries/surreal/schema_utils.html module src/allographer/schema_builder/queries/surreal/schema_utils 0 +nim notAllowedOption schema_builder/queries/surreal/schema_utils.html#notAllowedOption,string,string,string proc notAllowedOption(option, typ, column: string) 14 +nim notAllowedType schema_builder/queries/surreal/schema_utils.html#notAllowedType,string proc notAllowedType(typ: string) 18 +nim shouldRun schema_builder/queries/surreal/schema_utils.html#shouldRun,SurrealConnections,Table,string,bool proc shouldRun(rdb: SurrealConnections; table: Table; checksum: string; isReset: bool): bool 38 +nim exec schema_builder/queries/surreal/schema_utils.html#exec,SurrealConnections,seq[string] proc exec(rdb: SurrealConnections; queries: seq[string]) 49 +nim execThenSaveHistory schema_builder/queries/surreal/schema_utils.html#execThenSaveHistory,SurrealConnections,string,seq[string],string proc execThenSaveHistory(rdb: SurrealConnections; tableName: string;\n queries: seq[string]; checksum: string) 67 +nim execThenSaveHistory schema_builder/queries/surreal/schema_utils.html#execThenSaveHistory,SurrealConnections,string,string,string proc execThenSaveHistory(rdb: SurrealConnections; tableName: string; query: string;\n checksum: string) 100 +nimgrp execthensavehistory schema_builder/queries/surreal/schema_utils.html#execThenSaveHistory-procs-all proc 67 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.html new file mode 100644 index 0000000..89a84d6 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.html @@ -0,0 +1,115 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/sub/create_column_query + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/sub/create_column_query

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+ +
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.idx new file mode 100644 index 0000000..8ffc506 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/sub/create_column_query.idx @@ -0,0 +1,2 @@ +nimTitle create_column_query schema_builder/queries/surreal/sub/create_column_query.html module src/allographer/schema_builder/queries/surreal/sub/create_column_query 0 +nim createColumnString schema_builder/queries/surreal/sub/create_column_query.html#createColumnString,Table,Column proc createColumnString(table: Table; column: Column): seq[string] 533 diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.html b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.html new file mode 100644 index 0000000..0f43bf0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.html @@ -0,0 +1,153 @@ + + + + + + + +src/allographer/schema_builder/queries/surreal/surreal_query_type + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/queries/surreal/surreal_query_type

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Types

+
+
+
SurrealSchema = ref object
+  rdb*: SurrealConnections
+  table*: Table
+  column*: Column
+
+ + + +
+
+ +
+
+
+

Procs

+
+
+
+
proc new(_: type SurrealSchema; rdb: SurrealConnections; table: Table): SurrealSchema
+
+ + + +
+
+
+
proc new(_: type SurrealSchema; rdb: SurrealConnections; table: Table;
+         column: Column): SurrealSchema
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.idx b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.idx new file mode 100644 index 0000000..b00cf37 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/queries/surreal/surreal_query_type.idx @@ -0,0 +1,5 @@ +nimTitle surreal_query_type schema_builder/queries/surreal/surreal_query_type.html module src/allographer/schema_builder/queries/surreal/surreal_query_type 0 +nim SurrealSchema schema_builder/queries/surreal/surreal_query_type.html#SurrealSchema type SurrealSchema 6 +nim new schema_builder/queries/surreal/surreal_query_type.html#new,typeSurrealSchema,SurrealConnections,Table proc new(_: type SurrealSchema; rdb: SurrealConnections; table: Table): SurrealSchema 11 +nim new schema_builder/queries/surreal/surreal_query_type.html#new,typeSurrealSchema,SurrealConnections,Table,Column proc new(_: type SurrealSchema; rdb: SurrealConnections; table: Table; column: Column): SurrealSchema 14 +nimgrp new schema_builder/queries/surreal/surreal_query_type.html#new-procs-all proc 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.html new file mode 100644 index 0000000..56edc10 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/mariadb/alter + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mariadb/alter

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc alter(rdb: MariadbConnections; tables: varargs[Table]) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.idx new file mode 100644 index 0000000..941f3e8 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/alter.idx @@ -0,0 +1,2 @@ +nimTitle alter schema_builder/usecases/mariadb/alter.html module src/allographer/schema_builder/usecases/mariadb/alter 0 +nim alter schema_builder/usecases/mariadb/alter.html#alter,MariadbConnections,varargs[Table] proc alter(rdb: MariadbConnections; tables: varargs[Table]) 16 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.html new file mode 100644 index 0000000..61cb0ad --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/mariadb/create + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mariadb/create

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc create(rdb: MariadbConnections; tables: varargs[Table]) {....raises: [
+    KeyError, DbError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.idx new file mode 100644 index 0000000..f438230 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create.idx @@ -0,0 +1,2 @@ +nimTitle create schema_builder/usecases/mariadb/create.html module src/allographer/schema_builder/usecases/mariadb/create 0 +nim create schema_builder/usecases/mariadb/create.html#create,MariadbConnections,varargs[Table] proc create(rdb: MariadbConnections; tables: varargs[Table]) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.html new file mode 100644 index 0000000..737feea --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.html @@ -0,0 +1,125 @@ + + + + + + + +src/allographer/schema_builder/usecases/mariadb/create_query_def + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mariadb/create_query_def

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: MariadbConnections; table: Table): MariadbSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc createSchema(rdb: MariadbConnections; table: Table; column: Column): MariadbSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.idx new file mode 100644 index 0000000..9b84a6a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_query_def.idx @@ -0,0 +1,4 @@ +nimTitle create_query_def schema_builder/usecases/mariadb/create_query_def.html module src/allographer/schema_builder/usecases/mariadb/create_query_def 0 +nim createSchema schema_builder/usecases/mariadb/create_query_def.html#createSchema,MariadbConnections,Table proc createSchema(rdb: MariadbConnections; table: Table): MariadbSchema 7 +nim createSchema schema_builder/usecases/mariadb/create_query_def.html#createSchema,MariadbConnections,Table,Column proc createSchema(rdb: MariadbConnections; table: Table; column: Column): MariadbSchema 10 +nimgrp createschema schema_builder/usecases/mariadb/create_query_def.html#createSchema-procs-all proc 7 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.html new file mode 100644 index 0000000..576ee1a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.html @@ -0,0 +1,119 @@ + + + + + + + +src/allographer/schema_builder/usecases/mariadb/create_schema + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mariadb/create_schema

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: MariadbConnections; schemaPath = ""): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+                    forbids: [].}
+
+ +

if schemaPath is not specified, the schema will be generated in the current directory

+

Default schema file name is getCurrentDir() / "schema.nim"

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.idx new file mode 100644 index 0000000..ee6b7e0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/create_schema.idx @@ -0,0 +1,2 @@ +nimTitle create_schema schema_builder/usecases/mariadb/create_schema.html module src/allographer/schema_builder/usecases/mariadb/create_schema 0 +nim createSchema schema_builder/usecases/mariadb/create_schema.html#createSchema,MariadbConnections,string proc createSchema(rdb: MariadbConnections; schemaPath = ""): owned(Future[void]) 80 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.html new file mode 100644 index 0000000..6af727f --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/mariadb/drop + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mariadb/drop

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc drop(rdb: MariadbConnections; tables: varargs[Table]) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.idx new file mode 100644 index 0000000..cffd70c --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mariadb/drop.idx @@ -0,0 +1,2 @@ +nimTitle drop schema_builder/usecases/mariadb/drop.html module src/allographer/schema_builder/usecases/mariadb/drop 0 +nim drop schema_builder/usecases/mariadb/drop.html#drop,MariadbConnections,varargs[Table] proc drop(rdb: MariadbConnections; tables: varargs[Table]) 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.html new file mode 100644 index 0000000..01d9267 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/usecases/mysql/alter + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mysql/alter

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc alter(rdb: MysqlConnections; tables: varargs[Table]) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, JsonParsingError,
+    CatchableError], tags: [ReadIOEffect, TimeEffect, RootEffect,
+                            WriteDirEffect, ReadDirEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.idx new file mode 100644 index 0000000..8cb40aa --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/alter.idx @@ -0,0 +1,2 @@ +nimTitle alter schema_builder/usecases/mysql/alter.html module src/allographer/schema_builder/usecases/mysql/alter 0 +nim alter schema_builder/usecases/mysql/alter.html#alter,MysqlConnections,varargs[Table] proc alter(rdb: MysqlConnections; tables: varargs[Table]) 16 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.html new file mode 100644 index 0000000..7e868bb --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/usecases/mysql/create + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mysql/create

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc create(rdb: MysqlConnections; tables: varargs[Table]) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, JsonParsingError,
+    CatchableError], tags: [ReadIOEffect, TimeEffect, RootEffect,
+                            WriteDirEffect, ReadDirEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.idx new file mode 100644 index 0000000..ec2d86d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create.idx @@ -0,0 +1,2 @@ +nimTitle create schema_builder/usecases/mysql/create.html module src/allographer/schema_builder/usecases/mysql/create 0 +nim create schema_builder/usecases/mysql/create.html#create,MysqlConnections,varargs[Table] proc create(rdb: MysqlConnections; tables: varargs[Table]) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.html new file mode 100644 index 0000000..a1f407d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.html @@ -0,0 +1,125 @@ + + + + + + + +src/allographer/schema_builder/usecases/mysql/create_query_def + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mysql/create_query_def

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: MysqlConnections; table: Table): MysqlSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc createSchema(rdb: MysqlConnections; table: Table; column: Column): MysqlSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.idx new file mode 100644 index 0000000..3dfd204 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_query_def.idx @@ -0,0 +1,4 @@ +nimTitle create_query_def schema_builder/usecases/mysql/create_query_def.html module src/allographer/schema_builder/usecases/mysql/create_query_def 0 +nim createSchema schema_builder/usecases/mysql/create_query_def.html#createSchema,MysqlConnections,Table proc createSchema(rdb: MysqlConnections; table: Table): MysqlSchema 7 +nim createSchema schema_builder/usecases/mysql/create_query_def.html#createSchema,MysqlConnections,Table,Column proc createSchema(rdb: MysqlConnections; table: Table; column: Column): MysqlSchema 10 +nimgrp createschema schema_builder/usecases/mysql/create_query_def.html#createSchema-procs-all proc 7 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.html new file mode 100644 index 0000000..5d4d1b6 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.html @@ -0,0 +1,119 @@ + + + + + + + +src/allographer/schema_builder/usecases/mysql/create_schema + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mysql/create_schema

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: MysqlConnections; schemaPath = ""): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+                    forbids: [].}
+
+ +

if schemaPath is not specified, the schema will be generated in the current directory

+

Default schema file name is getCurrentDir() / "schema.nim"

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.idx new file mode 100644 index 0000000..6c9adc6 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/create_schema.idx @@ -0,0 +1,2 @@ +nimTitle create_schema schema_builder/usecases/mysql/create_schema.html module src/allographer/schema_builder/usecases/mysql/create_schema 0 +nim createSchema schema_builder/usecases/mysql/create_schema.html#createSchema,MysqlConnections,string proc createSchema(rdb: MysqlConnections; schemaPath = ""): owned(Future[void]) 80 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.html new file mode 100644 index 0000000..56ce6b4 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/usecases/mysql/drop + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/mysql/drop

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc drop(rdb: MysqlConnections; tables: varargs[Table]) {....raises: [KeyError,
+    DbError, ValueError, Exception, OSError, IOError, JsonParsingError,
+    CatchableError], tags: [ReadIOEffect, TimeEffect, RootEffect,
+                            WriteDirEffect, ReadDirEffect, WriteIOEffect],
+    forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.idx new file mode 100644 index 0000000..b06d561 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/mysql/drop.idx @@ -0,0 +1,2 @@ +nimTitle drop schema_builder/usecases/mysql/drop.html module src/allographer/schema_builder/usecases/mysql/drop 0 +nim drop schema_builder/usecases/mysql/drop.html#drop,MysqlConnections,varargs[Table] proc drop(rdb: MysqlConnections; tables: varargs[Table]) 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.html new file mode 100644 index 0000000..97fd852 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/postgres/alter + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/postgres/alter

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc alter(rdb: PostgresConnections; tables: varargs[Table]) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.idx new file mode 100644 index 0000000..2456552 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/alter.idx @@ -0,0 +1,2 @@ +nimTitle alter schema_builder/usecases/postgres/alter.html module src/allographer/schema_builder/usecases/postgres/alter 0 +nim alter schema_builder/usecases/postgres/alter.html#alter,PostgresConnections,varargs[Table] proc alter(rdb: PostgresConnections; tables: varargs[Table]) 16 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.html new file mode 100644 index 0000000..9e22570 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/postgres/create + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/postgres/create

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc create(rdb: PostgresConnections; tables: varargs[Table]) {....raises: [
+    DbError, KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.idx new file mode 100644 index 0000000..33edd3a --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create.idx @@ -0,0 +1,2 @@ +nimTitle create schema_builder/usecases/postgres/create.html module src/allographer/schema_builder/usecases/postgres/create 0 +nim create schema_builder/usecases/postgres/create.html#create,PostgresConnections,varargs[Table] proc create(rdb: PostgresConnections; tables: varargs[Table]) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.html new file mode 100644 index 0000000..fde1978 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.html @@ -0,0 +1,125 @@ + + + + + + + +src/allographer/schema_builder/usecases/postgres/create_query_def + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/postgres/create_query_def

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: PostgresConnections; table: Table): PostgresSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc createSchema(rdb: PostgresConnections; table: Table; column: Column): PostgresSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.idx new file mode 100644 index 0000000..2e9d3be --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_query_def.idx @@ -0,0 +1,4 @@ +nimTitle create_query_def schema_builder/usecases/postgres/create_query_def.html module src/allographer/schema_builder/usecases/postgres/create_query_def 0 +nim createSchema schema_builder/usecases/postgres/create_query_def.html#createSchema,PostgresConnections,Table proc createSchema(rdb: PostgresConnections; table: Table): PostgresSchema 7 +nim createSchema schema_builder/usecases/postgres/create_query_def.html#createSchema,PostgresConnections,Table,Column proc createSchema(rdb: PostgresConnections; table: Table; column: Column): PostgresSchema 10 +nimgrp createschema schema_builder/usecases/postgres/create_query_def.html#createSchema-procs-all proc 7 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.html new file mode 100644 index 0000000..10bb18f --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.html @@ -0,0 +1,119 @@ + + + + + + + +src/allographer/schema_builder/usecases/postgres/create_schema + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/postgres/create_schema

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: PostgresConnections; schemaPath = ""): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+                    forbids: [].}
+
+ +

if schemaPath is not specified, the schema will be generated in the current directory

+

Default schema file name is getCurrentDir() / "schema.nim"

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.idx new file mode 100644 index 0000000..fb557d3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/create_schema.idx @@ -0,0 +1,2 @@ +nimTitle create_schema schema_builder/usecases/postgres/create_schema.html module src/allographer/schema_builder/usecases/postgres/create_schema 0 +nim createSchema schema_builder/usecases/postgres/create_schema.html#createSchema,PostgresConnections,string proc createSchema(rdb: PostgresConnections; schemaPath = ""): owned(Future[void]) 82 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.html new file mode 100644 index 0000000..c323f2b --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/postgres/drop + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/postgres/drop

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc drop(rdb: PostgresConnections; tables: varargs[Table]) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.idx new file mode 100644 index 0000000..b81e969 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/postgres/drop.idx @@ -0,0 +1,2 @@ +nimTitle drop schema_builder/usecases/postgres/drop.html module src/allographer/schema_builder/usecases/postgres/drop 0 +nim drop schema_builder/usecases/postgres/drop.html#drop,PostgresConnections,varargs[Table] proc drop(rdb: PostgresConnections; tables: varargs[Table]) 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.html new file mode 100644 index 0000000..bce8453 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/usecases/sqlite/alter + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/sqlite/alter

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc alter(rdb: SqliteConnections; tables: varargs[Table]) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, RegexError,
+    CatchableError], tags: [ReadIOEffect, TimeEffect, RootEffect,
+                            WriteDirEffect, ReadDirEffect, WriteIOEffect,
+                            ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.idx new file mode 100644 index 0000000..d91c39e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/alter.idx @@ -0,0 +1,2 @@ +nimTitle alter schema_builder/usecases/sqlite/alter.html module src/allographer/schema_builder/usecases/sqlite/alter 0 +nim alter schema_builder/usecases/sqlite/alter.html#alter,SqliteConnections,varargs[Table] proc alter(rdb: SqliteConnections; tables: varargs[Table]) 16 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.html new file mode 100644 index 0000000..6a278a3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/sqlite/create + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/sqlite/create

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc create(rdb: SqliteConnections; tables: varargs[Table]) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.idx new file mode 100644 index 0000000..e827372 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create.idx @@ -0,0 +1,2 @@ +nimTitle create schema_builder/usecases/sqlite/create.html module src/allographer/schema_builder/usecases/sqlite/create 0 +nim create schema_builder/usecases/sqlite/create.html#create,SqliteConnections,varargs[Table] proc create(rdb: SqliteConnections; tables: varargs[Table]) 12 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.html new file mode 100644 index 0000000..249f780 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.html @@ -0,0 +1,125 @@ + + + + + + + +src/allographer/schema_builder/usecases/sqlite/create_query_def + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/sqlite/create_query_def

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: SqliteConnections; table: Table): SqliteSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc createSchema(rdb: SqliteConnections; table: Table; column: Column): SqliteSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.idx new file mode 100644 index 0000000..28a391e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_query_def.idx @@ -0,0 +1,4 @@ +nimTitle create_query_def schema_builder/usecases/sqlite/create_query_def.html module src/allographer/schema_builder/usecases/sqlite/create_query_def 0 +nim createSchema schema_builder/usecases/sqlite/create_query_def.html#createSchema,SqliteConnections,Table proc createSchema(rdb: SqliteConnections; table: Table): SqliteSchema 7 +nim createSchema schema_builder/usecases/sqlite/create_query_def.html#createSchema,SqliteConnections,Table,Column proc createSchema(rdb: SqliteConnections; table: Table; column: Column): SqliteSchema 10 +nimgrp createschema schema_builder/usecases/sqlite/create_query_def.html#createSchema-procs-all proc 7 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.html new file mode 100644 index 0000000..01716d1 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.html @@ -0,0 +1,119 @@ + + + + + + + +src/allographer/schema_builder/usecases/sqlite/create_schema + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/sqlite/create_schema

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: SqliteConnections; schemaPath = ""): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect,
+    ReadDbEffect], forbids: [].}
+
+ +

if schemaPath is not specified, the schema will be generated in the current directory

+

Default schema file name is getCurrentDir() / "schema.nim"

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.idx new file mode 100644 index 0000000..7a377f8 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/create_schema.idx @@ -0,0 +1,2 @@ +nimTitle create_schema schema_builder/usecases/sqlite/create_schema.html module src/allographer/schema_builder/usecases/sqlite/create_schema 0 +nim createSchema schema_builder/usecases/sqlite/create_schema.html#createSchema,SqliteConnections,string proc createSchema(rdb: SqliteConnections; schemaPath = ""): owned(Future[void]) 78 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.html new file mode 100644 index 0000000..4fbb94e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.html @@ -0,0 +1,117 @@ + + + + + + + +src/allographer/schema_builder/usecases/sqlite/drop + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/sqlite/drop

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc drop(rdb: SqliteConnections; tables: varargs[Table]) {....raises: [DbError,
+    KeyError, ValueError, Exception, OSError, IOError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect, ReadDbEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.idx new file mode 100644 index 0000000..fd217ba --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sqlite/drop.idx @@ -0,0 +1,2 @@ +nimTitle drop schema_builder/usecases/sqlite/drop.html module src/allographer/schema_builder/usecases/sqlite/drop 0 +nim drop schema_builder/usecases/sqlite/drop.html#drop,SqliteConnections,varargs[Table] proc drop(rdb: SqliteConnections; tables: varargs[Table]) 11 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.html new file mode 100644 index 0000000..248aa1f --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.html @@ -0,0 +1,114 @@ + + + + + + + +src/allographer/schema_builder/usecases/sub/migration_table_def + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/sub/migration_table_def

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Lets

+
+
+
migrationTable: Table = table("_allographer_migrations", [
+    increments(Column, "index"), string(Column, "name", 256),
+    text(Column, "query"), index(string(Column, "checksum", 256)),
+    index(datetime(Column, "created_at")), boolean(Column, "status")], "")
+
+ + + +
+
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.idx new file mode 100644 index 0000000..2583662 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/sub/migration_table_def.idx @@ -0,0 +1,2 @@ +nimTitle migration_table_def schema_builder/usecases/sub/migration_table_def.html module src/allographer/schema_builder/usecases/sub/migration_table_def 0 +nim migrationTable schema_builder/usecases/sub/migration_table_def.html#migrationTable let migrationTable 4 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.html new file mode 100644 index 0000000..42a6d56 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/usecases/surreal/alter + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/surreal/alter

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc alter(rdb: SurrealConnections; tables: varargs[Table]) {....raises: [
+    ValueError, Exception, OSError, IOError, KeyError, DbError, CatchableError,
+    HttpRequestError, LibraryError, SslError, ProtocolError, DbError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.idx new file mode 100644 index 0000000..f007ef7 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/alter.idx @@ -0,0 +1,2 @@ +nimTitle alter schema_builder/usecases/surreal/alter.html module src/allographer/schema_builder/usecases/surreal/alter 0 +nim alter schema_builder/usecases/surreal/alter.html#alter,SurrealConnections,varargs[Table] proc alter(rdb: SurrealConnections; tables: varargs[Table]) 16 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.html new file mode 100644 index 0000000..c375141 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/usecases/surreal/create + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/surreal/create

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc create(rdb: SurrealConnections; tables: varargs[Table]) {....raises: [
+    ValueError, Exception, OSError, IOError, KeyError, DbError,
+    HttpRequestError, LibraryError, SslError, ProtocolError, CatchableError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.idx new file mode 100644 index 0000000..7555b0d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create.idx @@ -0,0 +1,2 @@ +nimTitle create schema_builder/usecases/surreal/create.html module src/allographer/schema_builder/usecases/surreal/create 0 +nim create schema_builder/usecases/surreal/create.html#create,SurrealConnections,varargs[Table] proc create(rdb: SurrealConnections; tables: varargs[Table]) 13 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.html new file mode 100644 index 0000000..b1634d0 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.html @@ -0,0 +1,125 @@ + + + + + + + +src/allographer/schema_builder/usecases/surreal/create_query_def + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/surreal/create_query_def

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: SurrealConnections; table: Table): SurrealSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+
+
proc createSchema(rdb: SurrealConnections; table: Table; column: Column): SurrealSchema {.
+    ...raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.idx new file mode 100644 index 0000000..b5a451b --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_query_def.idx @@ -0,0 +1,4 @@ +nimTitle create_query_def schema_builder/usecases/surreal/create_query_def.html module src/allographer/schema_builder/usecases/surreal/create_query_def 0 +nim createSchema schema_builder/usecases/surreal/create_query_def.html#createSchema,SurrealConnections,Table proc createSchema(rdb: SurrealConnections; table: Table): SurrealSchema 7 +nim createSchema schema_builder/usecases/surreal/create_query_def.html#createSchema,SurrealConnections,Table,Column proc createSchema(rdb: SurrealConnections; table: Table; column: Column): SurrealSchema 10 +nimgrp createschema schema_builder/usecases/surreal/create_query_def.html#createSchema-procs-all proc 7 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.html new file mode 100644 index 0000000..4bbc522 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.html @@ -0,0 +1,119 @@ + + + + + + + +src/allographer/schema_builder/usecases/surreal/create_schema + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/surreal/create_schema

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc createSchema(rdb: SurrealConnections; schemaPath = ""): owned(
+    Future[void]) {....stackTrace: false, raises: [Exception], tags: [RootEffect,
+    WriteDirEffect, ReadDirEffect, ReadIOEffect, WriteIOEffect, TimeEffect],
+                    forbids: [].}
+
+ +

if schemaPath is not specified, the schema will be generated in the current directory

+

Default schema file name is getCurrentDir() / "schema.nim"

+ + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.idx new file mode 100644 index 0000000..0c3020e --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/create_schema.idx @@ -0,0 +1,2 @@ +nimTitle create_schema schema_builder/usecases/surreal/create_schema.html module src/allographer/schema_builder/usecases/surreal/create_schema 0 +nim createSchema schema_builder/usecases/surreal/create_schema.html#createSchema,SurrealConnections,string proc createSchema(rdb: SurrealConnections; schemaPath = ""): owned(Future[void]) 84 diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.html b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.html new file mode 100644 index 0000000..f9ebc7d --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.html @@ -0,0 +1,118 @@ + + + + + + + +src/allographer/schema_builder/usecases/surreal/drop + + + + + + + + + + + + + + + + +
+
+

src/allographer/schema_builder/usecases/surreal/drop

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+ +
+

Procs

+
+
+
+
proc drop(rdb: SurrealConnections; tables: varargs[Table]) {....raises: [
+    ValueError, Exception, OSError, IOError, KeyError, DbError, CatchableError,
+    HttpRequestError, LibraryError, SslError, ProtocolError], tags: [
+    ReadIOEffect, TimeEffect, RootEffect, WriteDirEffect, ReadDirEffect,
+    WriteIOEffect], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.idx b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.idx new file mode 100644 index 0000000..1760d10 --- /dev/null +++ b/clients/nim-allographer/docs/v2/schema_builder/usecases/surreal/drop.idx @@ -0,0 +1,2 @@ +nimTitle drop schema_builder/usecases/surreal/drop.html module src/allographer/schema_builder/usecases/surreal/drop 0 +nim drop schema_builder/usecases/surreal/drop.html#drop,SurrealConnections,varargs[Table] proc drop(rdb: SurrealConnections; tables: varargs[Table]) 11 diff --git a/clients/nim-allographer/docs/v2/theindex.html b/clients/nim-allographer/docs/v2/theindex.html new file mode 100644 index 0000000..99b3c46 --- /dev/null +++ b/clients/nim-allographer/docs/v2/theindex.html @@ -0,0 +1,5569 @@ + + + + + + + +Index + + + + + + + + + + + + + + + + +
+
+

Index

+ Modules: connection, env, query_builder, query_builder/enums, query_builder/error, query_builder/libs/database_url, query_builder/libs/mariadb/mariadb_impl, query_builder/libs/mariadb/mariadb_lib, query_builder/libs/mariadb/mariadb_rdb, query_builder/libs/mysql/mysql_impl, query_builder/libs/mysql/mysql_lib, query_builder/libs/mysql/mysql_rdb, query_builder/libs/postgres/postgres_impl, query_builder/libs/postgres/postgres_lib, query_builder/libs/postgres/postgres_rdb, query_builder/libs/sqlite/sqlite_impl, query_builder/libs/sqlite/sqlite_lib, query_builder/libs/sqlite/sqlite_rdb, query_builder/libs/surreal/surreal_impl, query_builder/libs/surreal/surreal_lib, query_builder/libs/surreal/surreal_rdb, query_builder/log, query_builder/models/database_types, query_builder/models/mariadb/mariadb_exec, query_builder/models/mariadb/mariadb_open, query_builder/models/mariadb/mariadb_query, query_builder/models/mariadb/mariadb_transaction, query_builder/models/mariadb/mariadb_types, query_builder/models/mariadb/query/mariadb_builder, query_builder/models/mariadb/query/mariadb_generator, query_builder/models/mysql/mysql_exec, query_builder/models/mysql/mysql_open, query_builder/models/mysql/mysql_query, query_builder/models/mysql/mysql_transaction, query_builder/models/mysql/mysql_types, query_builder/models/mysql/query/mysql_builder, query_builder/models/mysql/query/mysql_generator, query_builder/models/orm, query_builder/models/postgres/poatgres_transaction, query_builder/models/postgres/postgres_exec, query_builder/models/postgres/postgres_open, query_builder/models/postgres/postgres_query, query_builder/models/postgres/postgres_types, query_builder/models/postgres/query/postgres_builder, query_builder/models/postgres/query/postgres_generator, query_builder/models/sqlite/query/sqlite_builder, query_builder/models/sqlite/query/sqlite_generator, query_builder/models/sqlite/sqlite_exec, query_builder/models/sqlite/sqlite_open, query_builder/models/sqlite/sqlite_query, query_builder/models/sqlite/sqlite_transaction, query_builder/models/sqlite/sqlite_types, query_builder/models/surreal/query/surreal_builder, query_builder/models/surreal/query/surreal_generator, query_builder/models/surreal/surreal_exec, query_builder/models/surreal/surreal_open, query_builder/models/surreal/surreal_query, query_builder/models/surreal/surreal_types, schema_builder, schema_builder/enums, schema_builder/models/column, schema_builder/models/table, schema_builder/queries/mariadb/add_column, schema_builder/queries/mariadb/change_column, schema_builder/queries/mariadb/create_migration_table, schema_builder/queries/mariadb/create_table, schema_builder/queries/mariadb/drop_column, schema_builder/queries/mariadb/drop_table, schema_builder/queries/mariadb/mariadb_query_type, schema_builder/queries/mariadb/rename_column, schema_builder/queries/mariadb/rename_table, schema_builder/queries/mariadb/reset_table, schema_builder/queries/mariadb/schema_utils, schema_builder/queries/mariadb/sub/add_column_query, schema_builder/queries/mariadb/sub/change_column_query, schema_builder/queries/mariadb/sub/create_column_query, schema_builder/queries/mysql/add_column, schema_builder/queries/mysql/change_column, schema_builder/queries/mysql/create_migration_table, schema_builder/queries/mysql/create_table, schema_builder/queries/mysql/drop_column, schema_builder/queries/mysql/drop_table, schema_builder/queries/mysql/mysql_query_type, schema_builder/queries/mysql/rename_column, schema_builder/queries/mysql/rename_table, schema_builder/queries/mysql/reset_table, schema_builder/queries/mysql/schema_utils, schema_builder/queries/mysql/sub/add_column_query, schema_builder/queries/mysql/sub/change_column_query, schema_builder/queries/mysql/sub/create_column_query, schema_builder/queries/mysql/sub/is_exists, schema_builder/queries/postgres/add_column, schema_builder/queries/postgres/change_column, schema_builder/queries/postgres/create_migration_table, schema_builder/queries/postgres/create_table, schema_builder/queries/postgres/drop_column, schema_builder/queries/postgres/drop_table, schema_builder/queries/postgres/postgres_query_type, schema_builder/queries/postgres/rename_column, schema_builder/queries/postgres/rename_table, schema_builder/queries/postgres/reset_table, schema_builder/queries/postgres/schema_utils, schema_builder/queries/postgres/sub/add_column_query, schema_builder/queries/postgres/sub/change_column_query, schema_builder/queries/postgres/sub/create_column_query, schema_builder/queries/sqlite/add_column, schema_builder/queries/sqlite/change_column, schema_builder/queries/sqlite/create_migration_table, schema_builder/queries/sqlite/create_table, schema_builder/queries/sqlite/drop_column, schema_builder/queries/sqlite/drop_table, schema_builder/queries/sqlite/rename_column, schema_builder/queries/sqlite/rename_table, schema_builder/queries/sqlite/reset_table, schema_builder/queries/sqlite/schema_utils, schema_builder/queries/sqlite/sqlite_query_type, schema_builder/queries/sqlite/sub/add_column_query, schema_builder/queries/sqlite/sub/create_column_query, schema_builder/queries/surreal/add_column, schema_builder/queries/surreal/change_column, schema_builder/queries/surreal/create_migration_table, schema_builder/queries/surreal/create_sequence_table, schema_builder/queries/surreal/create_table, schema_builder/queries/surreal/drop_column, schema_builder/queries/surreal/drop_table, schema_builder/queries/surreal/rename_column, schema_builder/queries/surreal/rename_table, schema_builder/queries/surreal/reset_table, schema_builder/queries/surreal/schema_utils, schema_builder/queries/surreal/sub/create_column_query, schema_builder/queries/surreal/surreal_query_type, schema_builder/usecases/mariadb/alter, schema_builder/usecases/mariadb/create, schema_builder/usecases/mariadb/create_query_def, schema_builder/usecases/mariadb/create_schema, schema_builder/usecases/mariadb/drop, schema_builder/usecases/mysql/alter, schema_builder/usecases/mysql/create, schema_builder/usecases/mysql/create_query_def, schema_builder/usecases/mysql/create_schema, schema_builder/usecases/mysql/drop, schema_builder/usecases/postgres/alter, schema_builder/usecases/postgres/create, schema_builder/usecases/postgres/create_query_def, schema_builder/usecases/postgres/create_schema, schema_builder/usecases/postgres/drop, schema_builder/usecases/sqlite/alter, schema_builder/usecases/sqlite/create, schema_builder/usecases/sqlite/create_query_def, schema_builder/usecases/sqlite/create_schema, schema_builder/usecases/sqlite/drop, schema_builder/usecases/sub/migration_table_def, schema_builder/usecases/surreal/alter, schema_builder/usecases/surreal/create, schema_builder/usecases/surreal/create_query_def, schema_builder/usecases/surreal/create_schema, schema_builder/usecases/surreal/drop, utils, utils/snake_to_camel.

API symbols

+
`$`:
+
`%`:
+
`[]`:
+
`distinct`:
+
add:
+
AddColumn:
+
addColumn:
+
addColumnString:
+
addIndexString:
+
aggregate_context:
+
aggregate_count:
+
Alter:
+
alter:
+
alterAddForeignKey:
+
appendDbRowWithBaseColumns:
+
Asc:
+
asDatabaseUrl:
+
autoIncrement:
+
avg:
+
avgBuilder:
+
backup_finish:
+
backup_init:
+
backup_pagecount:
+
backup_remaining:
+
backup_step:
+
begin:
+
bigInteger:
+
binary:
+
BIND:
+
bind_blob:
+
bind_double:
+
bind_int:
+
bind_int64:
+
bind_null:
+
bindParam:
+
bind_parameter_count:
+
bind_parameter_index:
+
bind_parameter_name:
+
bind_text:
+
bind_text16:
+
boolean:
+
buildBaseDbColumns:
+
busy_handler:
+
busy_timeout:
+
Callback:
+
CASCADE:
+
change:
+
ChangeColumn:
+
changeColumn:
+
changeColumnString:
+
changeForeignKey:
+
changes:
+
ChangeTable:
+
char:
+
Charset_info_st:
+
checkError:
+
clear_bindings:
+
close:
+
CMDSTATUS_LEN:
+
Collate:
+
Collation:
+
collation_needed:
+
collation_needed16:
+
Collation_needed_func:
+
Column:
+
column_blob:
+
columnBuilder:
+
column_bytes:
+
column_bytes16:
+
column_count:
+
column_decltype:
+
column_decltype16:
+
column_double:
+
column_int:
+
column_int64:
+
ColumnMigrationType:
+
column_name:
+
column_name16:
+
columns:
+
column_table_name:
+
column_text:
+
column_text16:
+
column_type:
+
comment:
+
commit:
+
commit_hook:
+
complete:
+
complete16:
+
Connection:
+
CONNECTION_AUTH_OK:
+
CONNECTION_AWAITING_RESPONSE:
+
CONNECTION_BAD:
+
CONNECTION_CHECK_TARGET:
+
CONNECTION_CHECK_WRITABLE:
+
CONNECTION_CONSUME:
+
CONNECTION_GSS_STARTUP:
+
ConnectionInfo:
+
CONNECTION_MADE:
+
CONNECTION_NEEDED:
+
CONNECTION_OK:
+
Connections:
+
CONNECTION_SETENV:
+
CONNECTION_SSL_STARTUP:
+
CONNECTION_STARTED:
+
ConnStatusType:
+
count:
+
countBuilder:
+
Create:
+
create:
+
create_collation:
+
create_collation16:
+
Create_collation_func:
+
createColumnString:
+
createCommentColumn:
+
createForeignKey:
+
createForeignString:
+
create_function:
+
create_function16:
+
Create_function_final_func:
+
Create_function_func_func:
+
Create_function_step_func:
+
createIndexString:
+
createMigrationTable:
+
createSchema:
+
createSequenceTable:
+
CreateTable:
+
createTable:
+
createUpdatedAtString:
+
cuint:
+
Current:
+
CurrentOnUpdate:
+
DATA:
+
databaseName:
+
DatabaseUrl:
+
DatabaseUrlQuery:
+
data_count:
+
date:
+
datetime:
+
dbArray:
+
dbBindParamError:
+
dbBit:
+
dbBlob:
+
dbBool:
+
dbBox:
+
dbCircle:
+
DbColumn:
+
DbColumns:
+
dbComposite:
+
dbDate:
+
dbDatetime:
+
dbDecimal:
+
DbEffect:
+
dbEnum:
+
DbError:
+
dbError:
+
dbFixedChar:
+
dbFloat:
+
dbFormat:
+
dbGeometry:
+
dbInet:
+
dbInt:
+
dbJson:
+
dbLine:
+
dbLseg:
+
dbMacAddress:
+
dbNull:
+
dbOpen:
+
dbPath:
+
dbPoint:
+
dbPolygon:
+
DbRows:
+
dbSerial:
+
dbSet:
+
dbTime:
+
dbTimeInterval:
+
dbTimestamp:
+
DbType:
+
DbTypeKind:
+
dbUInt:
+
dbUnknown:
+
dbUrl:
+
dbUser1:
+
dbUser2:
+
dbUser3:
+
dbUser4:
+
dbUser5:
+
dbUuid:
+
dbVarchar:
+
dbXml:
+
decimal:
+
default:
+
DefaultDateTime:
+
delete:
+
deleteBuilder:
+
deleteByIdBuilder:
+
deleteByIdSql:
+
deleteSql:
+
Desc:
+
double:
+
Drop:
+
drop:
+
DropColumn:
+
dropColumn:
+
DropTable:
+
dropTable:
+
echoErrorMsg:
+
echoWarningMsg:
+
enumField:
+
Enum_field_types:
+
Enum_mysql_stmt_state:
+
Enum_server_command:
+
errcode:
+
errmsg:
+
errmsg16:
+
ERRMSG_SIZE:
+
error:
+
errorConnectionNum:
+
ERROR_MSG_LENGTH:
+
exec:
+
execGetValue:
+
ExecStatusType:
+
execThenSaveHistory:
+
false:
+
fetch:
+
fetch_field_direct:
+
fetch_row:
+
fetch_row_cont:
+
fetch_row_start:
+
fetchSql:
+
FIELD:
+
finalize:
+
find:
+
findPlain:
+
first:
+
firstPlain:
+
float:
+
foreign:
+
ForeignOnDelete:
+
free:
+
free_result:
+
free_result_cont:
+
free_result_start:
+
free_table:
+
fromArray:
+
fromObj:
+
fromObjArray:
+
fromSql:
+
get:
+
get_auxdata:
+
getColumns:
+
getColumnTypes:
+
getOsName:
+
getPlain:
+
get_socket:
+
get_table:
+
get_timeout_value:
+
get_timeout_value_ms:
+
gptr:
+
groupAllSql:
+
groupBy:
+
groupBySql:
+
having:
+
havingSql:
+
increments:
+
index:
+
info:
+
init:
+
INIT_COMMAND:
+
insert:
+
insertId:
+
insertSql:
+
insertValueBuilder:
+
insertValuesBuilder:
+
insertValueSql:
+
insertValuesSql:
+
InstantRow:
+
instantRows:
+
instantRowsPlain:
+
integer:
+
interrupt:
+
isConnected:
+
isExistsIndex:
+
isExistsMariadb:
+
isExistsMysql:
+
isExistsPostgres:
+
isExistsSqlite:
+
isExistsSurrealdb:
+
isExistsUnique:
+
join:
+
joinSql:
+
json:
+
last_insert_rowid:
+
leftJoin:
+
leftJoinSql:
+
len:
+
libversion:
+
libversion_number:
+
limit:
+
limitSql:
+
lo_close:
+
lo_creat:
+
lo_export:
+
logger:
+
LogSetting:
+
lo_import:
+
lo_lseek:
+
longText:
+
lo_open:
+
lo_read:
+
lo_tell:
+
lo_unlink:
+
lo_write:
+
MariaDB:
+
MariadbConnections:
+
MariadbParams:
+
MariadbParamValue:
+
MariadbQuery:
+
MariadbSchema:
+
max:
+
maxBuilder:
+
maxFromSql:
+
mediumInteger:
+
mediumText:
+
MEM_ROOT:
+
migrationTable:
+
min:
+
minBuilder:
+
mprintf:
+
my_bool:
+
my_socket:
+
MySQL:
+
MysqlConnections:
+
MYSQL_OPT_NONBLOCK:
+
MysqlParams:
+
MysqlParamValue:
+
MysqlQuery:
+
MysqlSchema:
+
MYSQL_WAIT_EXCEPT:
+
MYSQL_WAIT_READ:
+
MYSQL_WAIT_TIMEOUT:
+
MYSQL_WAIT_WRITE:
+
my_ulonglong:
+
NET:
+
new:
+
newRow:
+
NO_ACTION:
+
None:
+
notAllowedOption:
+
notAllowedType:
+
NOT_NULL_FLAG:
+
nullable:
+
Numeric:
+
num_fields:
+
numToAlphabet:
+
offset:
+
offsetSql:
+
Oid:
+
onDelete:
+
onTable:
+
open:
+
open16:
+
OPT_COMPRESS:
+
OPT_CONNECT_TIMEOUT:
+
OPT_GUESS_CONNECTION:
+
Option:
+
options:
+
optionsv:
+
OPT_LOCAL_INFILE:
+
OPT_NAMED_PIPE:
+
OPT_PROTOCOL:
+
OPT_READ_TIMEOUT:
+
OPT_RECONNECT:
+
OPT_USE_EMBEDDED_CONNECTION:
+
OPT_USE_REMOTE_CONNECTION:
+
OPT_USE_RESULT:
+
OPT_WRITE_TIMEOUT:
+
Order:
+
orderBy:
+
orderBySql:
+
orm:
+
orWhere:
+
orWhereSql:
+
parallel:
+
parallelSql:
+
parseDatabaseUrl:
+
ParsedDatabaseUrl:
+
PBIND:
+
Pcharset_info_st:
+
PConnStatusType:
+
Pcontext:
+
PDATA:
+
PExecStatusType:
+
PFIELD:
+
PGCancel:
+
PGconn:
+
PGlobjfuncs:
+
pgNotify:
+
PGParams:
+
PGresAttDesc:
+
PGresAttValue:
+
PGRES_BAD_RESPONSE:
+
PGRES_COMMAND_OK:
+
PGRES_COPY_BOTH:
+
PGRES_COPY_IN:
+
PGRES_COPY_OUT:
+
PGRES_EMPTY_QUERY:
+
PGRES_FATAL_ERROR:
+
PGRES_NONFATAL_ERROR:
+
PGRES_POLLING_ACTIVE:
+
PGRES_POLLING_FAILED:
+
PGRES_POLLING_OK:
+
PGRES_POLLING_READING:
+
PGRES_POLLING_WRITING:
+
PGRES_SINGLE_TUPLE:
+
PGRES_TUPLES_OK:
+
PGresult:
+
PGTransactionStatusType:
+
PGVerbosity:
+
ping:
+
Pmy_bool:
+
PMySQL:
+
PNET:
+
POid:
+
portOrDefault:
+
PostgresConnections:
+
PostgresPollingStatusType:
+
PostgreSQL:
+
PostgresQuery:
+
PostgresSchema:
+
PPByte:
+
PPGcancel:
+
PPGconn:
+
PPGlobjfuncs:
+
PPGNotify:
+
PPGresAttDesc:
+
PPGresAttValue:
+
PPGresult:
+
PPGTransactionStatusType:
+
PPGVerbosity:
+
PPostgresPollingStatusType:
+
PPPGresAttDesc:
+
PPPGresAttValue:
+
PPQArgBlock:
+
Ppqbool:
+
PPQconninfoOption:
+
PPQprintOpt:
+
PPSqlite3:
+
PPSqlite3_Backup:
+
PQArgBlock:
+
pqbackendPID:
+
pqbinaryTuples:
+
pqbool:
+
pqCancel:
+
pqclear:
+
pqclientEncoding:
+
pqcmdStatus:
+
pqcmdTuples:
+
pqconndefaults:
+
pqconnectdb:
+
pqconnectionNeedsPassword:
+
pqconnectionUsedPassword:
+
pqconnectPoll:
+
pqconnectStart:
+
pqconninfoFree:
+
PQconninfoOption:
+
pqconsumeInput:
+
pqdb:
+
pqdisplayTuples:
+
pqendcopy:
+
pqenv2encoding:
+
pqerrorMessage:
+
PQERRORS_DEFAULT:
+
PQERRORS_SQLSTATE:
+
PQERRORS_TERSE:
+
PQERRORS_VERBOSE:
+
pqescapeBytea:
+
pqescapeString:
+
pqexec:
+
pqexecParams:
+
pqexecPrepared:
+
pqfformat:
+
pqfinish:
+
pqflush:
+
pqfmod:
+
pqfn:
+
pqfname:
+
pqfnumber:
+
pqFreeCancel:
+
pqfreemem:
+
pqfsize:
+
pqftable:
+
pqftablecol:
+
pqftype:
+
pqGetCancel:
+
pqgetCopyData:
+
pqgetisnull:
+
pqgetlength:
+
pqgetline:
+
pqgetlineAsync:
+
pqgetResult:
+
pqgetvalue:
+
pqhost:
+
pqinitOpenSSL:
+
pqisBusy:
+
pqisnonblocking:
+
pqmakeEmptyPGresult:
+
pqmblen:
+
pqnfields:
+
PQnoticeProcessor:
+
PQnoticeReceiver:
+
pqnotifies:
+
pqntuples:
+
pqoidStatus:
+
pqoidValue:
+
pqoptions:
+
pqparameterStatus:
+
pqpass:
+
pqport:
+
pqprepare:
+
pqprint:
+
PQprintOpt:
+
pqprintTuples:
+
pqprotocolVersion:
+
pqputCopyData:
+
pqputCopyEnd:
+
pqputline:
+
pqputnbytes:
+
pqrequestCancel:
+
pqreset:
+
pqresetPoll:
+
pqresetStart:
+
pqresStatus:
+
pqresultErrorField:
+
pqresultErrorMessage:
+
pqresultStatus:
+
pqsendPrepare:
+
pqsendQuery:
+
pqsendQueryParams:
+
pqsendQueryPrepared:
+
pqserverVersion:
+
pqsetClientEncoding:
+
pqsetdb:
+
pqsetdbLogin:
+
pqsetErrorVerbosity:
+
pqsetnonblocking:
+
pqsetNoticeProcessor:
+
pqsetNoticeReceiver:
+
pqSetSingleRowMode:
+
pqsocket:
+
pqstatus:
+
pqtrace:
+
pqtransactionStatus:
+
PQTRANS_ACTIVE:
+
PQTRANS_IDLE:
+
PQTRANS_INERROR:
+
PQTRANS_INTRANS:
+
PQTRANS_UNKNOWN:
+
pqtty:
+
pqunescapeBytea:
+
pquntrace:
+
pquser:
+
prepare:
+
prepare16:
+
preparedExec:
+
preparedQuery:
+
prepare_v2:
+
PRES:
+
PRI_KEY_FLAG:
+
progress_handler:
+
PROWS:
+
PSqlite3:
+
PSqlite3_Backup:
+
Pst_dynamic_array:
+
PSTMT:
+
PStmt:
+
Pst_mysql:
+
Pst_mysql_methods:
+
Pst_mysql_rows:
+
Pst_mysql_stmt:
+
Pst_used_mem:
+
PUSED_MEM:
+
PValue:
+
PValueArg:
+
PVIO:
+
query:
+
queryPlain:
+
questionToDaller:
+
quote:
+
raw:
+
rawExec:
+
rawId:
+
RawMariadbQuery:
+
RawMysqlQuery:
+
RawPostgresQuery:
+
rawQuery:
+
RawSqliteQuery:
+
RawSurrealQuery:
+
rdbBigInteger:
+
rdbBinary:
+
rdbBoolean:
+
rdbChar:
+
rdbDate:
+
rdbDatetime:
+
rdbDecimal:
+
rdbDouble:
+
rdbEnumField:
+
rdbFloat:
+
rdbForeign:
+
rdbIncrements:
+
rdbInteger:
+
rdbJson:
+
rdbLongText:
+
rdbMediumInteger:
+
rdbMediumText:
+
rdbSmallInteger:
+
rdbSoftDelete:
+
rdbStrForeign:
+
rdbString:
+
rdbText:
+
rdbTime:
+
rdbTimestamp:
+
rdbTimestamps:
+
RdbTypeKind:
+
rdbUuid:
+
ReadDbEffect:
+
READ_DEFAULT_FILE:
+
READ_DEFAULT_GROUP:
+
read_query_result:
+
read_query_result_cont:
+
read_query_result_start:
+
real_connect:
+
real_connect_cont:
+
real_connect_start:
+
real_escape_string:
+
real_query:
+
real_query_cont:
+
real_query_start:
+
reference:
+
RenameColumn:
+
renameColumn:
+
RenameTable:
+
renameTable:
+
renameTo:
+
REPORT_DATA_TRUNCATION:
+
requireDatabaseUrlScheme:
+
RES:
+
reset:
+
resetMigrationTable:
+
resetSequence:
+
resetTable:
+
RESTRICT:
+
result_blob:
+
result_double:
+
result_error:
+
result_error16:
+
Result_func:
+
result_int:
+
result_int64:
+
result_null:
+
result_text:
+
result_text16:
+
result_text16be:
+
result_text16le:
+
result_value:
+
rollback:
+
ROW:
+
Row:
+
ROWS:
+
SCRAMBLE_LENGTH:
+
SECURE_AUTH:
+
seeder:
+
select:
+
selectAvgBuilder:
+
selectAvgSql:
+
selectBuilder:
+
selectByIdSql:
+
selectCountSql:
+
selectFindBuilder:
+
selectFirstBuilder:
+
selectFirstSql:
+
selectMaxSql:
+
selectMinSql:
+
selectSql:
+
selectSumBuilder:
+
selectSumSql:
+
set_authorizer:
+
set_auxdata:
+
SET_CHARSET_DIR:
+
SET_CHARSET_NAME:
+
SET_CLIENT_IP:
+
setColumnInfo:
+
SET_NULL:
+
setRow:
+
SHARED_MEMORY_BASE_NAME:
+
shouldRun:
+
smallInteger:
+
smallName:
+
snakeToCamel:
+
snprintf:
+
SockAddr:
+
softDelete:
+
SQLite3:
+
sqlite3_sleep:
+
SQLITE_ABORT:
+
SQLITE_ALTER_TABLE:
+
SQLITE_ANY:
+
SQLITE_ATTACH:
+
SQLITE_AUTH:
+
SQLITE_BLOB:
+
SQLITE_BUSY:
+
SQLITE_CANTOPEN:
+
SqliteConnections:
+
SQLITE_CONSTRAINT:
+
SQLITE_COPY:
+
SQLITE_CORRUPT:
+
SQLITE_CREATE_INDEX:
+
SQLITE_CREATE_TABLE:
+
SQLITE_CREATE_TEMP_INDEX:
+
SQLITE_CREATE_TEMP_TABLE:
+
SQLITE_CREATE_TEMP_TRIGGER:
+
SQLITE_CREATE_TEMP_VIEW:
+
SQLITE_CREATE_TRIGGER:
+
SQLITE_CREATE_VIEW:
+
sqliteDatabasePath:
+
SQLITE_DELETE:
+
SQLITE_DENY:
+
SQLITE_DETACH:
+
SQLITE_DETERMINISTIC:
+
SQLITE_DONE:
+
SQLITE_DROP_INDEX:
+
SQLITE_DROP_TABLE:
+
SQLITE_DROP_TEMP_INDEX:
+
SQLITE_DROP_TEMP_TABLE:
+
SQLITE_DROP_TEMP_TRIGGER:
+
SQLITE_DROP_TEMP_VIEW:
+
SQLITE_DROP_TRIGGER:
+
SQLITE_DROP_VIEW:
+
SQLITE_EMPTY:
+
SQLITE_ERROR:
+
SQLITE_FLOAT:
+
SQLITE_FORMAT:
+
SQLITE_FULL:
+
SQLITE_IGNORE:
+
SQLITE_INSERT:
+
SQLITE_INTEGER:
+
SQLITE_INTERNAL:
+
SQLITE_INTERRUPT:
+
SQLITE_IOERR:
+
sqliteLengthCheck:
+
SQLITE_LOCKED:
+
SQLITE_MISMATCH:
+
SQLITE_MISUSE:
+
SQLITE_NOLFS:
+
SQLITE_NOMEM:
+
SQLITE_NOTADB:
+
SQLITE_NOTFOUND:
+
SQLITE_NULL:
+
SQLITE_OK:
+
SQLITE_PERM:
+
SQLITE_PRAGMA:
+
SQLITE_PROTOCOL:
+
SqliteQuery:
+
sqliteQuoteIdent:
+
SQLITE_RANGE:
+
SQLITE_READ:
+
SQLITE_READONLY:
+
SQLITE_REINDEX:
+
SQLITE_ROW:
+
SQLITE_SCHEMA:
+
SqliteSchema:
+
SQLITE_SELECT:
+
SQLITE_STATIC:
+
SQLITE_TEXT:
+
SQLITE_TOOBIG:
+
SQLITE_TRANSACTION:
+
SQLITE_TRANSIENT:
+
SQLITE_UPDATE:
+
SQLITE_UTF16:
+
SQLITE_UTF16BE:
+
SQLITE_UTF16LE:
+
SQLITE_UTF8:
+
SQLSTATE_LENGTH:
+
start:
+
startSql:
+
Status:
+
STATUS_GET_RESULT:
+
STATUS_READY:
+
STATUS_USE_RESULT:
+
St_dynamic_array:
+
step:
+
St_mem_root:
+
STMT:
+
STMT_EXECUTE_DONE:
+
STMT_FETCH_DONE:
+
stmt_init:
+
STMT_INIT_DONE:
+
STMT_PREPARE_DONE:
+
St_mysql:
+
St_mysql_bind:
+
St_mysql_data:
+
St_mysql_field:
+
St_mysql_methods:
+
St_mysql_options:
+
St_mysql_res:
+
St_mysql_rows:
+
St_mysql_stmt:
+
St_net:
+
store_result_cont:
+
store_result_start:
+
strForeign:
+
string:
+
St_used_mem:
+
sum:
+
sumBuilder:
+
SurrealConn:
+
SurrealConnections:
+
SurrealDB:
+
SurrealId:
+
SurrealImpl:
+
SurrealQuery:
+
SurrealSchema:
+
table:
+
Table:
+
table:
+
TableMigrationType:
+
Tbind_destructor_func:
+
text:
+
time:
+
timestamp:
+
timestamps:
+
toSchema:
+
total_changes:
+
trace:
+
transaction:
+
true:
+
TYPE_BIT:
+
TYPE_BLOB:
+
TYPE_DATE:
+
TYPE_DATETIME:
+
TYPE_DECIMAL:
+
TYPE_DOUBLE:
+
TYPE_ENUM:
+
TYPE_FLOAT:
+
TYPE_GEOMETRY:
+
TYPE_INT24:
+
TYPE_LONG:
+
TYPE_LONG_BLOB:
+
TYPE_LONGLONG:
+
TYPE_MEDIUM_BLOB:
+
TYPE_NEWDATE:
+
TYPE_NEWDECIMAL:
+
TYPE_NULL:
+
TYPE_SET:
+
TYPE_SHORT:
+
TYPE_STRING:
+
TYPE_TIME:
+
TYPE_TIMESTAMP:
+
TYPE_TINY:
+
TYPE_TINY_BLOB:
+
TYPE_VARCHAR:
+
TYPE_VAR_STRING:
+
TYPE_YEAR:
+
U:
+
unique:
+
unsigned:
+
update:
+
updateBuilder:
+
updateMergeBuilder:
+
updateMergeSql:
+
updateSql:
+
updateValuesSql:
+
UsecaseType:
+
USED_MEM:
+
user_data:
+
use_result:
+
uuid:
+
value_blob:
+
value_bytes:
+
value_bytes16:
+
value_double:
+
value_int:
+
value_int64:
+
value_text:
+
value_text16:
+
value_text16be:
+
value_text16le:
+
value_type:
+
version:
+
where:
+
whereBetween:
+
whereBetweenSql:
+
whereBetweenStringSql:
+
whereIn:
+
whereInSql:
+
whereNotBetween:
+
whereNotBetweenSql:
+
whereNotBetweenStringSql:
+
whereNotIn:
+
whereNotInSql:
+
whereNull:
+
whereNullSql:
+
whereSql:
+
WriteDbEffect:
+
+ +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/utils.html b/clients/nim-allographer/docs/v2/utils.html new file mode 100644 index 0000000..40faf62 --- /dev/null +++ b/clients/nim-allographer/docs/v2/utils.html @@ -0,0 +1,105 @@ + + + + + + + +src/allographer/utils + + + + + + + + + + + + + + + + +
+
+

src/allographer/utils

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Procs

+
+
+
+
func getOsName(): string {....raises: [], tags: [], forbids: [].}
+
+ + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/utils.idx b/clients/nim-allographer/docs/v2/utils.idx new file mode 100644 index 0000000..9f94bb3 --- /dev/null +++ b/clients/nim-allographer/docs/v2/utils.idx @@ -0,0 +1,2 @@ +nimTitle utils utils.html module src/allographer/utils 0 +nim getOsName utils.html#getOsName proc getOsName(): string 3 diff --git a/clients/nim-allographer/docs/v2/utils/snake_to_camel.html b/clients/nim-allographer/docs/v2/utils/snake_to_camel.html new file mode 100644 index 0000000..55e686c --- /dev/null +++ b/clients/nim-allographer/docs/v2/utils/snake_to_camel.html @@ -0,0 +1,105 @@ + + + + + + + +src/allographer/utils/snake_to_camel + + + + + + + + + + + + + + + + +
+
+

src/allographer/utils/snake_to_camel

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

+
+

Procs

+
+
+
+
proc snakeToCamel(s: string): string {....raises: [], tags: [], forbids: [].}
+
+ + convert snake_case to CamelCase + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + diff --git a/clients/nim-allographer/docs/v2/utils/snake_to_camel.idx b/clients/nim-allographer/docs/v2/utils/snake_to_camel.idx new file mode 100644 index 0000000..203899f --- /dev/null +++ b/clients/nim-allographer/docs/v2/utils/snake_to_camel.idx @@ -0,0 +1,2 @@ +nimTitle snake_to_camel utils/snake_to_camel.html module src/allographer/utils/snake_to_camel 0 +nim snakeToCamel utils/snake_to_camel.html#snakeToCamel,string proc snakeToCamel(s: string): string 5 diff --git a/clients/nim-allographer/documents/rdb/benchmark_result.md b/clients/nim-allographer/documents/rdb/benchmark_result.md new file mode 100644 index 0000000..000d80e --- /dev/null +++ b/clients/nim-allographer/documents/rdb/benchmark_result.md @@ -0,0 +1,35 @@ +## index + + * [index](#index) + * [0.23.4](#0234) + + + + + + +## 0.23.4 +```sh +cd example +DB_POSTGRES=true nim c -r -d:release --threads:off -d:danger --mm:orc benchmark +``` + +query +|num|time| +|---|---| +|1|0.938770656| +|2|6.643012469| +|3|6.493994112999999| +|4|6.292020136999998| +|5|6.618309597| +|Avg|5.3972213944| + +update +|num|time| +|---|---| +|1|0.3599897400000001| +|2|0.2508196720000004| +|3|0.1847369050000012| +|4|0.2267158040000012| +|5|0.2636497700000007| +|Avg|0.2571823782000007| diff --git a/clients/nim-allographer/documents/rdb/postgres_efficiency_report.md b/clients/nim-allographer/documents/rdb/postgres_efficiency_report.md new file mode 100644 index 0000000..0af0638 --- /dev/null +++ b/clients/nim-allographer/documents/rdb/postgres_efficiency_report.md @@ -0,0 +1,288 @@ +# PostgreSQL driver 効率改善レポート + +MariaDB driver に対して実施した効率改善と同様の観点で、PostgreSQL driver(`src/allographer/query_builder/libs/postgres/` および `models/postgres/`)を調査した結果をまとめる。 + +--- + +## P0: 重大(即時修正推奨) + +### 1. `exec` / `insertId` が DML のたびに `information_schema.columns` を問い合わせている + +**ファイル:** `models/postgres/postgres_exec.nim` 196-211行, 214-229行 + +```nim +proc exec(self:PostgresQuery, queryString:string) {.async.} = + ... + let table = self.query["table"].getStr + let columnGetQuery = &"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '{table}'" + let (columns, _) = postgres_impl.query(..., columnGetQuery, ...).await + postgres_impl.exec(..., queryString, ..., columns, ...).await +``` + +**問題:** +- INSERT / UPDATE / DELETE のたびに追加で1回 SELECT が発生し、DML 性能が実質2倍遅くなる +- `'{table}'` の文字列補間は SQL インジェクションリスクがある(`insertId` にも同様の問題) + +**改善案:** +MariaDB で実施済みの `columnTypeCache: Table[string, seq[seq[string]]]` を `Connections` に追加し、テーブルごとに初回のみ取得・キャッシュする。SQL はパラメータ化クエリに変更する。 + +--- + +## P1: 中程度 + +### 2. `waiters` が `seq[Future[void]]` で先頭削除が O(n) + +**ファイル:** `models/postgres/postgres_types.nim` 20行, `models/postgres/postgres_exec.nim` 28-35行 + +```nim +# postgres_types.nim +waiters*: seq[Future[void]] + +# postgres_exec.nim +proc wakeOnePoolWaiter(pools: Connections) = + while pools.waiters.len > 0: + let w = pools.waiters[0] + pools.waiters.delete(0) # ← O(n) のシフト操作 + ... +``` + +**問題:** `seq` の先頭削除は全要素シフトを伴う O(n) 操作。waiter が多い高負荷時に性能劣化する。`removePoolWaiter` も同じくリニアスキャン+シフト。 + +**改善案:** `std/deques` の `Deque[Future[void]]` に変更。`popFirst()` が O(1)。MariaDB で実施済み。 + +### 3. `PgWaitState` が `ref object`(ヒープアロケーション) + +**ファイル:** `libs/postgres/postgres_impl.nim` 14行 + +```nim +type + PgWaitState = ref object + cancelled: bool +``` + +**問題:** `waitPgReadable` / `waitPgWritable` が呼ばれるたびに `PgWaitState` のヒープアロケーションが発生する。 + +**改善案:** `object`(値型)に変更。async proc のクロージャ環境に格納されるため、値型でも正しく動作する。MariaDB で実施済み。 + +### 4. `waitPgReadable` と `waitPgWritable` がほぼ重複している + +**ファイル:** `libs/postgres/postgres_impl.nim` 37-81行 + +```nim +proc waitPgReadable(db: PPGconn, timeoutMs: int): Future[bool] {.async.} = + ... + addRead(fd, readCb) # ← 違いはここだけ + ... + +proc waitPgWritable(db: PPGconn, timeoutMs: int): Future[bool] {.async.} = + ... + addWrite(fd, writeCb) # ← 違いはここだけ + ... +``` + +**問題:** 2つの proc の差は `addRead` vs `addWrite` だけ。20行以上の完全な重複。 + +**改善案:** `waitPgIo(db, timeoutMs, forRead: bool)` のような統一 proc にまとめる。 + +### 5. `waitPgReadable` / `waitPgWritable` が `pqsocket` を二重に呼んでいる + +**ファイル:** `libs/postgres/postgres_impl.nim` 37-58行 + +```nim +proc waitPgReadable(db: PPGconn, timeoutMs: int): Future[bool] {.async.} = + ... + ensurePgSocketRegistered(db) # 内部で pqsocket(db) を呼ぶ + let sock = pqsocket(db) # ← 再度 pqsocket を呼んでいる + if sock < 0: + dbError(db) + let fd = AsyncFD(cint(sock)) + ... +``` + +**問題:** `ensurePgSocketRegistered` 内で `pqsocket` を呼んで fd を得ているのに、その戻り値を使わず直後にもう一度 `pqsocket` を呼んでいる。FFI 呼び出しの無駄。 + +**改善案:** `ensurePgSocketRegistered` を `AsyncFD` を返す proc にする(MariaDB 版 `ensureMariadbSocketRegistered` と同じ設計)。 + +--- + +## P2: 軽微 + +### 6. `getTime().toUnix()` によるデッドライン計算(秒精度) + +**ファイル:** `libs/postgres/postgres_impl.nim` 83-89行, 149-150行 他多数 + +```nim +proc pgRemainingMs(deadline: int64): int = + let leftSec = deadline - getTime().toUnix() + ... + result = int(leftSec * 1000) + +# deadline の生成(秒精度) +let calledAt = getTime().toUnix() +let deadline = calledAt + timeout.int64 +``` + +**問題:** +- `getTime().toUnix()` は秒精度のため、ミリ秒単位のタイムアウト制御ができない +- NTP 補正によるクロック巻き戻りの影響を受ける +- `timeout = 1` のケースでは、0.9秒経過後に残り 0ms と判定される可能性がある + +**改善案:** `std/monotimes` の `MonoTime` + `Duration` に統一する。MariaDB で実施済み。 + +### 7. `setColumnInfo` が行ごとに全カラム分の FFI 呼び出しを行っている + +**ファイル:** `libs/postgres/postgres_lib.nim` 159-167行, `libs/postgres/postgres_impl.nim` 160-163行 他 + +```nim +# postgres_lib.nim +proc setColumnInfo*(res: PPGresult; dbRows: var DbRows; line, cols: int32) = + var columns: DbColumns + setLen(columns, cols) + for col in 0'i32..cols-1: + columns[col].name = $pqfname(res, col) # 行に依存しない + columns[col].typ = getColumnType(res, line, col) # NULL判定のみ行依存 + columns[col].tableName = $(pqftable(res, col)) # 行に依存しない + dbRows.add(columns) + +# postgres_impl.nim(query* 内) +for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + setColumnInfo(pqresult, dbRows, i, cols) # ← 毎行で全カラム FFI 呼び出し + rows.add(row) +``` + +**問題:** `pqfname`, `pqftable`, `pqftype` はカラムの OID・名前・テーブル名を返す関数で、行によって結果が変わらない。行に依存するのは `pqgetisnull(res, line, col)` による NULL 判定のみ。1000行×10カラムでは、カラム名・テーブル名だけで2万回の不要な FFI 呼び出し+文字列アロケーションが発生する。 + +**改善案:** カラムの基本情報(name, tableName, OID)はループ外で1回だけ取得し、行ごとには NULL 判定のみ行う。 + +```nim +# 基本情報を1回だけ取得 +var baseColumns: DbColumns +setLen(baseColumns, cols) +for col in 0'i32..cols-1: + baseColumns[col].name = $pqfname(res, col) + baseColumns[col].typ = getBaseColumnType(res, col) # NULL判定なし + baseColumns[col].tableName = $(pqftable(res, col)) + +# 行ごとにNULLだけ上書き +for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + var rowColumns = baseColumns + for col in 0'i32..cols-1: + if pqgetisnull(res, i, col) == 1: + rowColumns[col].typ = DbType(kind: dbNull, name: "null") + dbRows.add(rowColumns) + rows.add(row) +``` + +### 8. `dbFormat` / `questionToDaller` の char-by-char 連結 + +**ファイル:** `libs/postgres/postgres_lib.nim` 203-216行, 219-229行 + +```nim +proc dbFormat*(formatstr: string, args: varargs[string]): string = + result = "" + ... + for c in items(formatstr): + if c == '?': + add(result, dbQuote(args[a])) + inc(a) + else: + add(result, c) # ← 1文字ずつ add + +proc questionToDaller*(s:string):string = + var i = 1 + for c in s: + if c == '?': + result.add(&"${i}") + i += 1 + else: + result.add(c) # ← 1文字ずつ add +``` + +**問題:** `?` が出現するまでの連続部分を1文字ずつ `add` しており、長いクエリ文字列で非効率。 + +**改善案:** `?` 位置間のサブ文字列を一括 `add` する。MariaDB の `dbFormat` で実施済みの手法。 + +### 9. `query*` / `rawQuery*` / `execGetValue*` のパラメータ送信パターンが重複 + +**ファイル:** `libs/postgres/postgres_impl.nim` 134-166行, 193-225行, 228-260行 + +```nim +# query*, execGetValue*, rawQuery* の冒頭がほぼ同一: +let status = + if pgParams.nParams > 0: + pqsendQueryParams(db, query.cstring, pgParams.nParams, nil, pgParams.values, ...) + else: + pqsendQueryParams(db, query.cstring, pgParams.nParams, nil, nil, nil, nil, 0) +defer: + if pgParams.nParams > 0: pgParams.values.deallocCStringArray() +if status != 1: dbError(db) +``` + +**問題:** 4箇所にわたるクエリ送信ボイラープレート。保守コストが高い。 + +**改善案:** `pgSendQuery(db, pgParams)` のような共通 proc を抽出する。 + +### 10. `fromObjArray` の2つのオーバーロードが大部分重複 + +**ファイル:** `libs/postgres/postgres_lib.nim` 239-291行, 293-337行 + +**問題:** `columns` パラメータの有無のみが異なり、JSON→string 変換ロジックは同一。 + +**改善案:** 共通の内部 proc を抽出し、`columns` が空の場合はバイナリ判定をスキップする。 + +--- + +## P3: 微小 + +### 11. `dbQuote` の容量見積もりなし + +**ファイル:** `libs/postgres/postgres_lib.nim` 182-201行 + +```nim +proc dbQuote(s: string): string = + if s == "null": + return "NULL" + result = "'" + ... +``` + +**問題:** `newStringOfCap` による事前容量確保がない。特殊文字が多い文字列で再アロケーションが発生する。 + +**改善案:** `result = newStringOfCap(s.len + 2)` を先頭に追加。 + +--- + +## 改善優先度まとめ + +| 優先度 | # | 問題 | 影響 | 修正難度 | +|---|---|---|---|---| +| **P0** | 1 | `exec`/`insertId` が毎回 `information_schema` を問い合わせ + SQL インジェクション | DML ごとに追加1クエリ | 中(キャッシュ + パラメータ化) | +| **P1** | 2 | `waiters` の `seq.delete(0)` が O(n) | 高負荷時の性能劣化 | 低(`Deque` に変更) | +| **P1** | 3 | `PgWaitState` が `ref object` | 毎回のヒープアロケーション | 低(`object` に変更) | +| **P1** | 4 | `waitPgReadable`/`waitPgWritable` が重複 | 保守コスト | 低(統一) | +| **P1** | 5 | `pqsocket` 二重呼び出し | 不要な FFI 呼び出し | 低 | +| **P2** | 6 | `getTime().toUnix()` の秒精度デッドライン | タイムアウト精度 | 低(`MonoTime` 化) | +| **P2** | 7 | `setColumnInfo` が行ごとに全カラム FFI 呼び出し | 行数×カラム数の不要な FFI + アロケーション | 中(構造変更) | +| **P2** | 8 | `dbFormat`/`questionToDaller` の char-by-char | 長いクエリで微小な差 | 低 | +| **P2** | 9 | クエリ送信ボイラープレート重複 | 保守コスト | 低 | +| **P2** | 10 | `fromObjArray` の重複 | 保守コスト | 低 | +| **P3** | 11 | `dbQuote` の容量見積もりなし | 微小 | 低 | + +--- + +## MariaDB との比較 + +| 項目 | MariaDB(修正済み) | PostgreSQL(現状) | +|---|---|---| +| カラム型キャッシュ | `columnTypeCache` 導入済み | 毎回 `information_schema` を問い合わせ | +| ブロッキング ping | 除去済み | `assert db.status == CONNECTION_OK`(FFI なし、問題小) | +| `setColumnInfo` 位置 | ループ外に移動済み | ループ内で毎行呼び出し | +| waiter データ構造 | `Deque` に変更済み | `seq`(先頭削除 O(n)) | +| 待機状態オブジェクト | 値型(`object`)に変更済み | `ref object`(ヒープ割当) | +| デッドライン | `MonoTime` に変更済み | `getTime().toUnix()`(秒精度) | +| `dbFormat` | セグメント一括 add | char-by-char | +| SQL インジェクション | プレースホルダ化済み | 文字列補間のまま | + +**注意:** PostgreSQL の `assert db.status == CONNECTION_OK` は `pqStatus()` がローカルのフィールド読み取りであり、MariaDB の `mysql_ping()` のようなネットワークラウンドトリップは発生しない。そのため、PostgreSQL 側はこの assert の除去は不要。 diff --git a/clients/nim-allographer/documents/rdb/query_builder.md b/clients/nim-allographer/documents/rdb/query_builder.md new file mode 100644 index 0000000..14adb53 --- /dev/null +++ b/clients/nim-allographer/documents/rdb/query_builder.md @@ -0,0 +1,773 @@ +Example: Query Builder for RDB +=== +[back](../../README.md) + +## index + +* [Example: Query Builder for RDB](#example-query-builder-for-rdb) + * [index](#index) + * [Create Connection](#create-connection) + * [SELECT](#select) + * [return JsonNode](#return-jsonnode) + * [return Object](#return-object) + * [get](#get) + * [first](#first) + * [find](#find) + * [columns](#columns) + * [join](#join) + * [where](#where) + * [orWhere](#orwhere) + * [whereBetween](#wherebetween) + * [whereNotBetween](#wherenotbetween) + * [whereIn](#wherein) + * [whereNotIn](#wherenotin) + * [whereNull](#wherenull) + * [groupBy_having](#groupby_having) + * [orderBy](#orderby) + * [limit_offset](#limit_offset) + * [paginate](#paginate) + * [fastPaginate](#fastpaginate) + * [INSERT](#insert) + * [Return ID Insert](#return-id-insert) + * [UPDATE](#update) + * [DELETE](#delete) + * [Plain Response](#plain-response) + * [Raw SQL](#raw-sql) + * [Prepared Statement](#prepared-statement) + * [Aggregates](#aggregates) + * [Transaction](#transaction) + + + + + +--- + +## Create Connection +```nim +import allographer/connection + +let maxConnections = 95 +let timeout = 30 + +# Using connection URL (Recommended) +let rdb = dbOpen(PostgreSQL, "postgresql://user:password@localhost:5432/database", maxConnections, timeout) + +# Optional pool aging controls +# let rdb = dbOpen( +# PostgreSQL, +# "postgresql://user:password@localhost:5432/database", +# maxConnections, +# timeout, +# maxConnectionLifetime = 300, +# maxConnectionIdleTime = 300, +# ) + +# Using positional arguments +# let rdb = dbOpen(PostgreSQL, "database", "user", "password", "localhost", 5432, maxConnections, timeout) + +# SQLite +# let rdb = dbOpen(SQLite3, "/path/to/db.sqlite3", maxConnections, timeout) +# let rdb = dbOpen(SQLite3, ":memory:", maxConnections, timeout) + +# MySQL / MariaDB +# let rdb = dbOpen(MySQL, "mysql://user:password@localhost:3306/database", maxConnections, timeout) +# let rdb = dbOpen(MariaDB, "mariadb://user:password@localhost:3306/database", maxConnections, timeout) +``` + +## SELECT +[to index](#index) + +When it returns following table + +|id|float|char|datetime|null|is_admin| +|---|---|---|---|---|---| +|1|3.14|char|2019-01-01 12:00:00.1234||1| + +### return JsonNode +```nim +import allographer/query_builder + +echo rdb.table("test") + .select("id", "float", "char", "datetime", "null", "is_admin") + .get() + .await +``` + +```nim +>> @[ + { + "id": 1, # JInt + "float": 3.14, # JFloat + "char": "char", # JString + "datetime": "2019-01-01 12:00:00.1234", # JString + "null": null # JNull + "is_admin": true # JBool + } +] +``` + +### return Object +If object is defined and set arg for `orm`, response will be an object as ORM + +```nim +import allographer/query_builder + +type Typ = ref object + id: int + float: float + char: string + datetime: string + null: string + is_admin: bool + +var rows = rdb.table("test") + .select("id", "float", "char", "datetime", "null", "is_admin") + .get() + .orm(Typ) + .await +``` + +```nim +echo rows[0].id +>> 1 # int + +echo rows[0].float +>> 3.14 # float + +echo rows[0].char +>> "char" # string + +echo rows[0].datetime +>> "2019-01-01 12:00:00.1234" # string + +echo rows[0].null +>> "" # string + +echo rows[0].is_admin +>> true # bool +``` + +If DB response is empty, `get` return empty seq, `find` and `first` return optional object. +```nim +let response = await rdb.table("test").get().orm(Typ).await +assert response.len == 0 + +let response = await rdb.raw("select * from users").get().orm(Typ).await +assert response.len == 0 + +let response = await rdb.table("test").find(1).orm(Typ).await +assert response.type == Option[Typ] +assert response.isSome == false + +let response = await rdb.table("test").first().orm(Typ).await +assert response.type == Option[Typ] +assert response.isSome == false +``` + +### get +Retrieving all row from a table +```nim +let users = rdb.table("users").get().await +for user in users: + echo user["name"] +``` + +### first +Retrieving a single row from a table. This returns `Option[JsonNode]` +```nim +let user = rdb + .table("users") + .where("name", "=", "John") + .first() + .await +if user.isSome: + echo user.get["name"] +``` + +### find +Retrieve a single row by its primary key. This returns `Option[JsonNode]` +```nim +let user = rdb.table("users").find(1).await +if user.isSome: + echo user.get["name"] +``` + +If the column name of a promary key is not "id", specify this in 2nd arg of `find` +```nim +let user = rdb.table("users").find(1, "user_id").await +if user.isSome: + echo user.get["name"] +``` + +### columns +Retrieve columns from table. +```nim +let columns = rdb.table("users").columns().await +columns == @["id", "name", "email"] +``` + + +### join +```nim +let users = rdb + .table("users") + .select("users.id", "contacts.phone", "orders.price") + .join("contacts", "users.id", "=", "contacts.user_id") + .join("orders", "users.id", "=", "orders.user_id") + .get() + .await +``` + +### where +```nim +let users = rdb.table("users").where("age", ">", 25).get().await +``` + +### orWhere +```nim +let users = rdb + .table("users") + .where("age", ">", 25) + .orWhere("name", "=", "John") + .get() + .await +``` + +### whereBetween +```nim +let users = rdb + .table("users") + .whereBetween("age", [25, 35]) + .get() + .await +``` + +### whereNotBetween +```nim +let users = rdb + .table("users") + .whereNotBetween("age", [25, 35]) + .get() + .await +``` + +### whereIn +```nim +let users = rdb + .table("users") + .whereIn("id", @[1, 2, 3]) + .get() + .await +``` + +### whereNotIn +```nim +let users = rdb + .table("users") + .whereNotIn("id", @[1, 2, 3]) + .get() + .await +``` + +### whereNull +```nim +let users = rdb + .table("users") + .whereNull("updated_at") + .get() + .await +``` + +### groupBy_having +```nim +let users = rdb + .table("users") + .group_by("count") + .having("count", ">", 100) + .get() + .await +``` + +### orderBy +```nim +let users = rdb + .table("users") + .orderBy("name", Desc) + .get() + .await +``` +2nd arg of `orderBy` is Enum. `Desc` or `Asc` + + +### limit_offset +```nim +let users = rdb + .table("users") + .offset(10) + .limit(5) + .get() + .await +``` + +### paginate +```nim +rdb.table("users").delete(2).await +let users = rdb + .table("users") + .select("id", "name") + .paginate(3, 1) + .await +``` +arg1... Numer of items per page +arg2... Numer of page(option)(1 is set by default) + +```nim +echo users +>> { + "count":3, + "currentPage":[ + {"id":1,"name":"user1"}, + {"id":3,"name":"user3"}, + {"id":4,"name":"user4"} + ], + "hasMorePages":true, + "lastPage":3, + "nextPage":2, + "perPage":3, + "previousPage":1, + "total":9 +} +``` + +|ATTRIBUTE|DESCRIPTION| +|---|---| +|count|number of results on the current page| +|currentPage|results of current page| +|hasMorePages|Returns `True` if there is more pages else `False`| +|lastPage|The number of the last page| +|nextPage|The number of the next page if it exists else equel to lastPage| +|perPage|The number of results per page| +|previousPage|The number of the previous page if it exists else 1| +|total|The total number of results| + + +### fastPaginate +It run faster than `paginate()` because it doesn't use `offset`. + +|sample URL|usage|result items| +|---|---|---| +|/users?items=5|`fastPaginate(5)`|1,2,3,4,5| +|/users?items=5&since=6|`fastPaginateNext(5, 6)`|6,7,8,9,10| +|/users?items=5&until=5|`fastPaginateBack(5, 5)`|1,2,3,4,5| + +```nim +proc fastPaginate(this:Rdb, display:int, key="id", order:Order=Asc): JsonNode +``` +- display...Numer of items per page. +- key...Name of a primary key column (option). default is `id`. +- order...Asc or Desc (option). default is `Asc`. + +```nim +proc fastPaginateNext(this:Rdb, display:int, id:int, key="id", order:Order=Asc): JsonNode + +proc fastPaginateBack(this:Rdb, display:int, id:int, key="id", order:Order=Asc): JsonNode +``` +- display...Numer of items per page. +- id...Value of primary key. It should be larger than 0. +- key...Name of a primary key column (option). default is `id`. +- order...Asc or Desc (option). default is `Asc`. + +```nim +var users = rdb.table("users").select("id", "name").fastPaginate(3).await + +>> { + "previousId":0, + "hasPreviousId": false, + "currentPage":[ + {"id":1,"name":"user1"}, + {"id":2,"name":"user2"}, + {"id":3,"name":"user3"}, + ], + "nextId":4, + "hasNextId": true +} +``` +```nim +users = rdb.table("users") + .select("id", "name") + .fastPaginateNext(3, users["nextId"].getInt) + .await + +>> { + "previousId":4, + "hasPreviousId": true, + "currentPage":[ + {"id":5,"name":"user5"}, + {"id":6,"name":"user6"}, + {"id":7,"name":"user7"} + ], + "nextId":8, + "hasNextId": true +} +``` +```nim +users = rdb.table("users") + .select("id", "name") + .fastPaginateBack(3, users["previousId"].getInt) + .await + +>> { + "previousId":0, + "hasPreviousId": false, + "currentPage":[ + {"id":1,"name":"user1"}, + {"id":2,"name":"user2"}, + {"id":3,"name":"user3"}, + ], + "nextId":4, + "hasNextId": true +} +``` + +order Desc +```nim +echo rdb.table("users") + .select("id", "name") + .fastPaginateNext(3, 5, order=Desc) + .await + +>> { + "previousId":6, + "hasPreviousId":true, + "currentPage":[ + {"id":5,"name":"user5"}, + {"id":4,"name":"user4"}, + {"id":3,"name":"user3"} + ], + "nextId":2, + "hasNextId":true +} +``` + +paginate with `join` and `where` +```nim +echo rdb.table("users") + .select("users.id", "users.name", "users.auth_id") + .join("auth", "auth.id", "=", "users.auth_id") + .where("auth.id", "=", 2) + .fastPaginate(3, key="users.id") + .await + +>> { + "previousId":0, + "hasPreviousId":false, + "currentPage":[ + {"id":4,"name":"user4","auth_id":2}, + {"id":6,"name":"user6","auth_id":2}, + {"id":8,"name":"user8","auth_id":2} + ], + "nextId":8, + "hasNextId":true +} +``` + + +## INSERT +[to index](#index) + +```nim +import allographer/query_builder + +rdb.table("users").insert(%*{ + "name": "John", + "email": "John@gmail.com" +}) +.await + +>> INSERT INTO users (name, email) VALUES ("John", "John@gmail.com") +``` +```nim +import allographer/query_builder + +rdb.table("users").insert( + @[ + %*{"name": "John", "email": "John@gmail.com", "address": "London"}, + %*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"}, + %*{"name": "George", "email": "George@gmail.com", "address": "London"}, + ] +) +.await + +>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London"), ("Paul", "Paul@gmail.com", "London"), ("George", "George@gmail.com", "London") +``` +```nim +import allographer/query_builder + +echo rdb.table("users").insert( + @[ + %*{"name": "John", "email": "John@gmail.com", "address": "London"}, + %*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"}, + %*{"name": "George", "birth_date": "1943-02-25", "address": "London"}, + ] +) +.await + +>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London") +>> INSERT INTO users (name, email, address) VALUES ("Paul", "Paul@gmail.com", "London") +>> INSERT INTO users (name, birth_date, address) VALUES ("George", "1960-1-1", "London") +``` + +### Return ID Insert +```nim +import allographer/query_builder + +echo rdb.table("users").insertId(%*{ + "name": "John", + "email": "John@gmail.com" +}) +.await + +>> INSERT INTO users (name, email) VALUES ("John", "John@gmail.com") +>> 1 # ID of new row is return +``` +```nim +import allographer/query_builder + +echo rdb.table("users").insertId( + @[ + %*{"name": "John", "email": "John@gmail.com", "address": "London"}, + %*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"}, + %*{"name": "George", "email": "George@gmail.com", "address": "London"}, + ] +) +.await + +>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London"), ("Paul", "Paul@gmail.com", "London"), ("George", "George@gmail.com", "London") +>> @[1, 2] # Seq of ID of new row is return +``` +```nim +import allographer/query_builder + +echo rdb.table("users").insertId( + @[ + %*{"name": "John", "email": "John@gmail.com", "address": "London"}, + %*{"name": "Paul", "email": "Paul@gmail.com", "address": "London"}, + %*{"name": "George", "birth_date": "1943-02-25", "address": "London"}, + ] +) +.await + +>> INSERT INTO users (name, email, address) VALUES ("John", "John@gmail.com", "London") +>> INSERT INTO users (name, email, address) VALUES ("Paul", "Paul@gmail.com", "London") +>> INSERT INTO users (name, birth_date, address) VALUES ("George", "1960-1-1", "London") +>> @[1, 2, 3] # Seq of ID of new row is return +``` + + +## UPDATE +[to index](#index) + +```nim +import allographer/query_builder + +rdb +.table("users") +.where("id", "=", 100) +.update(%*{"name": "Mick", "address": "NY"}) +.await + +>> UPDATE users SET name = "Mick", address = "NY" WHERE id = 100 +``` + +## DELETE +[to index](#index) + +```nim +import allographer/query_builder + +rdb +.table("users") +.delete(1) +.await + +>> DELETE FROM users WHERE id = 1 +``` + +If column name of primary key is not exactory "id", you can specify it's name. + +```nim +import allographer/query_builder + +rdb +.table("users") +.delete(1, key="user_id") +.await + +>> DELETE FROM users WHERE user_id = 1 +``` + +```nim +import allographer/query_builder + +rdb +.table("users") +.where("address", "=", "London") +.delete() +.await + +>> DELETE FROM users WHERE address = "London" +``` + +## Plain Response +[to index](#INDEX) + +`Plain` response doesn't have it's column name but it run faster than `JsonNode` response + +```nim +echo rdb.table("users").get().await +>> @[ + %*{"id": 1, "name": "user1", "email": "user1@gmail.com"}, + %*{"id": 2, "name": "user2", "email": "user2@gmail.com"}, + %*{"id": 3, "name": "user3", "email": "user3@gmail.com"} +] + +echo rdb.table("users").getPlain().await +>> @[ + @["1", "user1", "user1@gmail.com"], + @["2", "user2", "user2@gmail.com"], + @["3", "user3", "user3@gmail.com"], +] +``` + +```nim +echo rdb.table("users").find(1).await +>> %*{"id": 1, "name": "user1", "email": "user1@gmail.com"} + +echo rdb.table("users").findPlain(1).await +>> @["1", "user1", "user1@gmail.com"] +``` + +```nim +echo rdb.table("users").first().await +>> %*{"id": 1, "name": "user1", "email": "user1@gmail.com"} + +echo rdb.table("users").firstPlain().await +>> @["1", "user1", "user1@gmail.com"] +``` + +## Raw SQL +[to index](#INDEX) + +```nim +import allographer/query_builder + +let sql = """ +SELECT ProductName + FROM Product + WHERE Id IN (SELECT ProductId + FROM OrderItem + WHERE Quantity > 100) +""" +echo rdb.raw(sql).get().await +echo rdb.raw(sql).getPlain().await +echo rdb.raw(sql).first().await +echo rdb.raw(sql).firstPlain().await +``` +```nim +let sql = "UPDATE users SET name = ? where id = ?" +rdb.raw(sql, "John", "1").exec().await +``` + +## Prepared Statement +[to index](#index) + +```nim +import allographer/query_builder + +let selectStmt = rdb.prepare("""SELECT "id", "name" FROM "users" WHERE "id" = ?""") +let updateStmt = rdb.prepare("""UPDATE "users" SET "name" = ? WHERE "id" = ?""") + +let row = await selectStmt.first(@["1"]) +await updateStmt.exec(@["John", "1"]) + +# close() is logical close. +await selectStmt.close() +await updateStmt.close() +``` + +SurrealDB では server-side prepare ではなく、`LET` を使った client-side template reuse として同じ API を提供します。 + +In PostgreSQL, a context API can be used to ensure multiple operations use the same connection. + +```nim +await rdb.withConn( + proc(ctx: PostgresPreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @["1"]) + await updateStmt.exec(ctx, @["John", "1"]) +) +``` + +In PostgreSQL, APIs to physically clear the prepared statement cache are also available. + +```nim +await rdb.flushStmt("""SELECT "id", "name" FROM "users" WHERE "id" = ?""") +await rdb.clearStmtCache() +``` + +## Aggregates +[to index](#index) + +Except of `count`, these functions return `Option` type. + +```nim +import allographer/query_builder + +echo rdb.table("users").count().await +>> 10 # int + +let response = await rdb.table("users").max("name").await +if response.isSome: + echo response.get +>> "user9" # string + +let response = await rdb.table("users").max("id").await +if response.isSome: + echo response.get +>> "10" # string + +let response = await rdb.table("users").min("name").await +if response.isSome: + echo response.get +>> "user1" # string + +let response = await rdb.table("users").min("id").await +if response.isSome: + echo response.get +>> "1" # string + +let response = await rdb.table("users").avg("id").await +if response.isSome: + echo response.get +>> 5.5 # float + +let response = await rdb.table("users").sum("id").await +if response.isSome: + echo response.get +>> 55.0 # float +``` + +## Transaction +[to index](#index) + +```nim +transaction: + var user = rdb.table("users").select("id").where("name", "=", "user3").first().await + if user.isSome: + var id = user.get["id"].getInt() + echo id + user = rdb.table("users").select("name", "email").find(id).await + if user.isSome: + echo user.get +``` +If all code in transaction block success, `COMMIT` is run otherwise `ROLLBACK` diff --git a/clients/nim-allographer/documents/rdb/schema_builder.md b/clients/nim-allographer/documents/rdb/schema_builder.md new file mode 100644 index 0000000..b0d88ce --- /dev/null +++ b/clients/nim-allographer/documents/rdb/schema_builder.md @@ -0,0 +1,248 @@ +Example: Schema Builder +=== +[back](../README.md) + +## index + +- [Example: Schema Builder](#example-schema-builder) + - [index](#index) + - [Create table](#create-table) + - [Alter Table](#alter-table) + - [add column](#add-column) + - [change column](#change-column) + - [drop column](#drop-column) + - [rename table](#rename-table) + - [drop table](#drop-table) + - [Migration history](#migration-history) + - [seeder template](#seeder-template) + - [integer](#integer) + - [float](#float) + - [char](#char) + - [date](#date) + - [others](#others) + - [options](#options) + - [Foreign Key Constraints](#foreign-key-constraints) + + + + + +--- + +## Create table +```nim +import allographer/schema_builder +import connections # or define rdb here + +rdb.create( + table("auth", [ + Column.increments("id"), + Column.uuid("uuid"), + Column.string("name"), + Column.timestamps() + ]), + table("users", [ + Column.increments("id"), + Column.string("name"), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL), + Column.strForeign("uuid").reference("uuid").onTable("auth").onDelete(SET_NULL) + ]) +) +``` + +## Alter Table +### add column +```nim +rdb.alter( + table("auth", [ + Column.string("email").add(), + ]), + table("users", [ + Column.string("email").unique().default("").add(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).add() + ]) +) +``` +`>> ALTER TABLE "users" ADD COLUMN 'email' VARCHAR UNIQUE DEFAULT '' CHECK (length("email") <= 255)` + + +### change column +```nim +rdb.alter( + table("users", [ + Column.renameColumn("name", "new_name"), + Column.char("name", 20).unique().default("").change() + ]) +) +``` +- Create new table with new column definition +- Rename table which you want to change column +- Copy table data from old table to new table +- Drop old table + + +### drop column +```nim +rdb.alter( + table("users", [ + Column.dropColumn("name") + ]) +) +``` + +### rename table +```nim +rdb.alter( + table("users").renameTo("new_users") +) +``` +`>> ALTER TABLE users RENAME TO new_users` + +### drop table +```nim +rdb.drop( + table("users") +) +``` +`>> DROP TABLE users` + +## Migration history +allographer generate `_allographer_migrations` table in your database. It has migration history data which have hash key generated a query. + + +```nim +# migrate.nim +import std/asyncdispatch +import std/json +import allographer/schema_builder +import connections + +rdb.create( + table("auth", [ + Column.increments("id"), + Column.string("name") + ]), + table("users", [ + Column.increments("id"), + Column.string("name"), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) +) + +seeder rdb, "auth": + rdb.table("auth").insert(@[ + %*{"name": "admin"}, + %*{"name": "member"} + ]) + .waitFor + +seeder rdb, "users": + var data: seq[JsonNode] + for i in 1..10: + data.add(%*{ + "name": &"user{i}", + "auth_id": if i mod 2 == 0: 1 else: 2 + }) + rdb.table("users").insert(data).waitFor +``` + +```sh +# first time +nim c -r migrate +>> run query and generate `_allographer_migrations` table +# secound time +nim c -r migrate +>> nothing to do +# reset existsing table and run migration again +nim c -r migrate --reset + or +nim c -d:reset -r migrate +>> drop table and create table +``` + +### seeder template +The `seeder` block allows the code in the block to work only when the table or specified column is empty. + +```nim +template seeder*(rdb:Rdb, tableName:string, body:untyped):untyped + +template seeder*(rdb:Rdb, tableName, column:string, body:untyped):untyped +``` + + +## integer +|Command|Description| +|---|---| +|`increments("id")`|Auto-incrementing UNSIGNED INTEGER (primary key) equivalent column.| +|`integer("votes")`|INTEGER equivalent column.| +|`smallInteger("votes")`|SMALLINT equivalent column| +|`mediumInteger("votes")`|MEDIUMINT equivalent column| +|`bigInteger("votes")`|UNSIGNED BIGINT equivalent column.| + +## float +|Command|Description| +|---|---| +|`decimal("amount", 8, 2)`|DECIMAL equivalent column with a precision (total digits) and scale (decimal digits).| +|`double("amount", 8, 2)`|DOUBLE equivalent column with a precision (total digits) and scale (decimal digits).| +|`float("float")`|FLOAT equivalent column with a implicit precision (total digits) and scale (decimal digits).| + +## char +|Command|Description| +|---|---| +|`uuid("name")`|VARCHAR indexed and unique column.| +|`char("name", 100)`|CHAR equivalent column with an optional length.| +|`string("name")`|VARCHAR equivalent column.| +|`string("name", 100)`|VARCHAR equivalent column with a optional length.| +|`text("description")`|TEXT equivalent column.| +|`mediumText("description")`|MEDIUMTEXT equivalent column.| +|`longText("description")`|LONGTEXT equivalent column.| + +## date +|Command|Description| +|---|---| +|`date("created_at")`|DATE equivalent column.| +|`datetime("created_at")`|DATETIME equivalent column.| +|`time("sunrise")`|TIME equivalent column.| +|`timestamp("added_on")`|TIMESTAMP equivalent column.| +|`timestamps()`|Adds nullable `created_at` and `updated_at` TIMESTAMP equivalent columns.| +|`softDelete()`|Adds a nullable `deleted_at` TIMESTAMP equivalent column for soft deletes.| + +## others +|Command|Description| +|---|---| +|`binary("data")`|BLOB equivalent column.| +|`boolean("confirmed")`|BOOLEAN equivalent column.| +|`enumField("level", ["easy", "hard"])`|ENUM equivalent column.| +|`json("options")`|JSON equivalent column.| + +## options +|Command|Description| +|---|---| +|`.nullable()`|Designate that the column allows NULL values| +|`.default(value)`|Declare a default value for a column| +|`.unsigned()`|Set INTEGER to UNSIGNED| +|`.unique()`|Adding an unique index| +|`.index()`|Adding an index| +|`.comment("value")`|Adding a comment to a column. Only for Postgres, Mariadb and Mysql| + +## Foreign Key Constraints +For example, let's define a `user_id` column on the table that references the `id` column on a `users` table: +```nim +Column.foreign("user_id") + .reference("id") + .onTable("users") + .onDelete(SET_NULL) + +Column.strForeign("uuid") + .reference("uuid") + .onTable("users") + .onDelete(SET_NULL) + +# use `onTable("users")` +``` + +arg of `onDelete` is enum +- RESTRICT +- CASCADE +- SET_NULL +- NO_ACTION diff --git a/clients/nim-allographer/documents/rdb/surreal_efficiency_report.md b/clients/nim-allographer/documents/rdb/surreal_efficiency_report.md new file mode 100644 index 0000000..914b8b6 --- /dev/null +++ b/clients/nim-allographer/documents/rdb/surreal_efficiency_report.md @@ -0,0 +1,222 @@ +# Surreal driver 効率改善レポート +> [!WARNING] +> SurrealDB support is planned for v3. The current implementation and proposed improvements are for reference only and will be addressed in v3. + +`src/allographer/query_builder/libs/surreal/` と、その呼び出し元である `models/surreal/` を調査し、`documents/rdb/postgres_efficiency_report.md` と `320-allographerpostgresql-非同期待機改善-設計書.mdc` と同じ観点で、処理効率と待機制御の改善余地を整理した。 + +MariaDB / PostgreSQL 側で既に改善済みのポイントと比べると、Surreal 側は以下の 2 点が特に目立つ。 + +- 接続プール待機はすでに `Deque` + `MonoTime` ベースで改善済み +- 一方で HTTP リクエスト自体の timeout 制御と、初期化時の重複 round-trip は未整理 + +--- + +## P0: 重大 + +### 1. `timeout` 引数が HTTP リクエスト経路で使われていない + +**ファイル:** `src/allographer/query_builder/libs/surreal/surreal_impl.nim` 47-112行 + +```nim +proc query*(db:SurrealConn, query: string, args: JsonNode, timeout:int):Future[JsonNode] {.async.} = + let query = dbFormat(query, args) + let resp = db.client.post(&"{db.host}:{db.port}/sql", query).await + let body = resp.body().await.parseJson() +``` + +**問題:** +- `query` / `exec` / `info` の全経路で `timeout` が未使用 +- 遅い HTTP 応答やハングした接続があると、その接続スロットを無期限に占有する +- `getFreeConn` 側は `MonoTime` で待機期限を持っていても、実リクエストが返らない限りコネクションは返却されず、スループットが崩れる +- PostgreSQL 設計書で整理した「待機制御を driver 内で完結させる」という方針と比べると、Surreal だけ実処理 timeout が抜けている + +**改善案:** +- `post()` と `body()` を `withTimeout` で包み、`MonoTime` ベースの deadline へ統一する +- もしくは `AsyncHttpClient` 側の timeout 設定を `dbOpen` で明示し、driver 引数 `timeout` と同期させる +- エラー整形も共通化し、timeout 時は `DbError` へ寄せる + +--- + +## P1: 中程度 + +### 2. `dbOpen` が接続数ぶん `/status` と `DEFINE NAMESPACE / DATABASE` を逐次実行している + +**ファイル:** `src/allographer/query_builder/models/surreal/surreal_open.nim` 17-35行 + +```nim +for i in 0.. 0: + ... + result = chr(int('A') + remainder) & result +... +return result.toLower() +``` + +**問題:** +- `& result` による前置連結で、文字数に応じた再アロケーションが発生する +- 最後に `toLower()` でもう 1 回文字列を作る +- 1 回のコストは小さいが、`dbFormat(JsonNode)` と `questionToDaller()` の両方から頻繁に呼ばれる + +**改善案:** +- 小さなバッファへ末尾追加して最後に reverse する +- もしくは最初から `'a'` 基準で生成して `toLower()` をなくす + +### 6. `questionToDaller()` が `numToAlphabet()` と組み合わさって placeholder 数ぶん文字列生成を行う + +**ファイル:** `src/allographer/query_builder/libs/surreal/surreal_lib.nim` 58-74行 + +```nim +if s[j] == '?': + ... + result.add('$') + result.add(numToAlphabet(i)) +``` + +**問題:** +- 文字列本体はセグメント単位で `add` できており悪くないが、placeholder 名生成が都度アロケーションになる +- `find()` 経路では `surreal_exec.nim` 366-369行で毎回 `questionToDaller` を通る + +**改善案:** +- `numToAlphabet` 改善とセットで最適化する +- placeholder 数が少ないケースでは効果は小さいため、P1 の `dbFormat(JsonNode)` ほど優先度は高くない + +### 7. `getAllRows()` が `JsonNode` 配列を `seq[JsonNode]` へ変換するため、結果件数ぶん追加走査が入る + +**ファイル:** `src/allographer/query_builder/models/surreal/surreal_exec.nim` 121-138行, 184-201行 + +```nim +let rows = surreal_impl.query(...).await +... +return rows.toSeq +``` + +**問題:** +- `surreal_impl.query()` はすでに `JsonNode` の配列を返している +- `get()` / `insertId(seq)` のたびに `toSeq()` でもう一度全件を走査する +- 大量行取得では余分なイテレーションと `seq` 生成が発生する + +**改善案:** +- 内部表現を `JsonNode` で統一して必要箇所だけ `seq` 化する +- あるいは `surreal_impl.query()` 側で最終的に必要な `seq[JsonNode]` を 1 回だけ組み立てる + +--- + +## すでに良い点 + +### 1. プール待機は `Deque` + 通知ベースで、PostgreSQL 改善後と同じ方向にそろっている + +**ファイル:** `src/allographer/query_builder/models/surreal/surreal_exec.nim` 24-75行 + +- `waiters` は `Deque[Future[void]]` +- `wakeOnePoolWaiter()` は `popFirst()` で O(1) +- `sleepAsync(10)` ポーリングは使っていない + +### 2. プール待機の deadline は `MonoTime` ベース + +**ファイル:** `src/allographer/query_builder/models/surreal/surreal_exec.nim` 40-69行 + +- PostgreSQL 改善設計書で問題視していた wall clock 依存は入っていない +- ただし「接続取得待ち」だけで、「HTTP 実行待ち」にはまだ広がっていない + +--- + +## 改善優先度まとめ + +| 優先度 | # | 問題 | 影響 | 修正難度 | +|---|---|---|---|---| +| **P0** | 1 | HTTP リクエストで `timeout` 未使用 | ハング時に接続が返却されず、プール全体が詰まる | 中 | +| **P1** | 2 | `dbOpen` が接続数ぶん逐次 bootstrap | 起動時間が `maxConnections` に比例 | 低〜中 | +| **P1** | 3 | `dbFormat(JsonNode)` の中間文字列多発 | bulk 系で CPU / アロケーション増 | 中 | +| **P1** | 4 | HTTP 実行ロジック 6 箇所重複 | 最適化と timeout 対応の実装コスト増 | 低 | +| **P2** | 5 | `numToAlphabet` の前置連結 + `toLower()` | placeholder 多いと微小劣化 | 低 | +| **P2** | 6 | `questionToDaller()` の placeholder 名生成 | 微小 | 低 | +| **P2** | 7 | `rows.toSeq()` の追加走査 | 大量行取得で余分な走査 | 低〜中 | + +--- + +## PostgreSQL / MariaDB との比較 + +| 項目 | MariaDB / PostgreSQL | Surreal(現状) | +|---|---|---| +| プール待機 | `Deque` + 通知ベース | 同様に改善済み | +| deadline 管理 | `MonoTime` 利用 | 接続取得待ちは利用、HTTP 実行待ちは未適用 | +| 実リクエスト timeout | driver 内で明示設計 | `timeout` 引数が未使用 | +| 初期化コスト | DB 接続確立中心 | `/status` + `DEFINE ...` を接続数ぶん HTTP 実行 | +| フォーマット最適化 | `dbFormat` 改善済み箇所あり | `JsonNode` 経路は未最適化 | + +## 次に着手するなら + +1. `surreal_impl.nim` に共通 `runSql` ヘルパを作り、timeout・HTTP status・JSON parse・Surreal error 判定を 1 箇所へ寄せる +2. `surreal_open.nim` の bootstrap を 1 回化し、残り接続は軽量に初期化する +3. `surreal_lib.nim` の `dbFormat(JsonNode)` を単一バッファ書き込みへ変更する diff --git a/clients/nim-allographer/documents/rdb/typeDifinition.md b/clients/nim-allographer/documents/rdb/typeDifinition.md new file mode 100644 index 0000000..ea991af --- /dev/null +++ b/clients/nim-allographer/documents/rdb/typeDifinition.md @@ -0,0 +1,60 @@ + +## index + + * [index](#index) + * [JInt](#jint) + * [JFloat](#jfloat) + * [JBool](#jbool) + + + + + +--- + +|allographer|sqlite|mysql|postgres|return type| +|---|---|---|---|---| +|increments|INTEGER|INT|INTEGER|JInt +|integer|INTEGER|INT|INTEGER|JInt +|smallInteger|INTEGER|SMALLINT|SMALLINT|JInt +|mediumInteger|INTEGER|MEDIUMINT|INTEGER|JInt +|bigInteger|INTEGER|BIGINT|BIGINT|JInt +|decimal|NUMERIC|DECIMAL|NUMERIC|JFloat +|double|NUMERIC|DOUBLE|NUMERIC|JFloat +|float|DOUBLE|DOUBLE|NUMERIC|JFloat +|char|VARCHAR|CHAR|CHAR|JString +|string|VARCHAR|VARCHAR|VARCHAR|JString +|uuid|VARCHAR|VARCHAR|VARCHAR|JString +|text|TEXT|TEXT|TEXT|JString +|mediumText|TEXT|MEDIUMTEXT|TEXT|JString +|longText|TEXT|LONGTEXT|TEXT|JString +|date|DATE|DATE|DATE|JString +|datetime|DATETIME|DATETIME|TIMESTAMP|JString +|time|TIME|TIME|TIME|JString +|timestamp|DATETIME|DATETIME|TIMESTAMP|JString +|timestamps|DATETIME|DATETIME|TIMESTAMP|JString +|softDelete|DATETIME|DATETIME|TIMESTAMP|JString +|binary|BLOB|BLOB|BYTEA|JString +|boolean|TINYINT|TINYINT|BOOLEAN|JBool +|enumField|VARCHAR|ENUM|CHARACTER|JString +|json|TEXT|JSON|JSON|JObject / JArray *1| + +*1: For SQLite, it returns `JString`. For other databases, it returns parsed `JsonNode` (`JObject` or `JArray`). +|foreign|INTEGER|INT|INT|JInt +|strForeign|VARCHAR|VARCHAR|VARCHAR|JString + + +## JInt +``` +["INTEGER", "INT", "SMALLINT", "MEDIUMINT", "BIGINT"] +``` + +## JFloat +``` +["NUMERIC", "DECIMAL", "DOUBLE"] +``` + +## JBool +``` +["TINYINT", "BOOLEAN"] +``` diff --git a/clients/nim-allographer/documents/surrealdb/prepared_statement.md b/clients/nim-allographer/documents/surrealdb/prepared_statement.md new file mode 100644 index 0000000..5bffefb --- /dev/null +++ b/clients/nim-allographer/documents/surrealdb/prepared_statement.md @@ -0,0 +1,77 @@ +Example: Prepared Statement for SurrealDB +=== +> [!WARNING] +> SurrealDB の prepared statement は server-side prepare ではなく、client-side template reuse と `/sql` の multi-statement 実行で再現しています。 + +[back](../../README.md) + +## index + +* [Example: Prepared Statement for SurrealDB](#example-prepared-statement-for-surrealdb) + * [index](#index) + * [About](#about) + * [Create Connection](#create-connection) + * [prepare](#prepare) + * [withConn](#withconn) + * [Cache Control](#cache-control) + + +--- + +## About +[SurrealDB official docs](https://surrealdb.com/docs) + +Prepared statement の公開 API は他 driver に寄せていますが、SurrealDB では内部的に次の形へ展開します。 + +```sql +LET $a = "user:alice"; +SELECT * FROM "user" WHERE "id" = $a; +``` + +`?` は `$a`, `$b`, ... に変換され、引数は `LET` で前置されます。これにより、1 リクエストで安全に再利用できるテンプレートとして扱えます。 + +## Create Connection + +```nim +import std/asyncdispatch +import allographer/connection + +let surreal = dbOpen(SurrealDB, "test", "test", "user", "pass", "http://surreal", 8000, 5, 30, false, false).await +``` + +## prepare + +```nim +import std/asyncdispatch +import std/json +import allographer/query_builder + +let stmt = surreal.prepare("""SELECT * FROM "user" WHERE "id" = ?""") +let rows = await stmt.get(@["user:alice"]) +let row = await stmt.first(@["user:alice"]) +await stmt.exec(@["user:alice"]) +await stmt.close() +``` + +`close()` は logical close です。SurrealDB 側に物理 prepared handle はないため、キャッシュの整理は `flushStmt()` / `clearStmtCache()` で行います。 + +## withConn + +```nim +await surreal.withConn( + proc(ctx: SurrealPreparedContext): Future[void] {.async.} = + discard await stmt.first(ctx, @["user:alice"]) + await stmt.exec(ctx, @["user:alice"]) +) +``` + +`withConn()` は同じ connection index を使い回したいときの API です。将来の session 前提最適化や transaction helper とも相性がよい形にしてあります。 + +## Cache Control + +```nim +await surreal.flushStmt(stmt) +await surreal.clearStmtCache() +``` + +`flushStmt(stmt)` はその prepared statement を閉じて、対応するテンプレートをキャッシュから外します。`clearStmtCache()` はキャッシュ全体を空にします。 diff --git a/clients/nim-allographer/documents/surrealdb/query_builder.md b/clients/nim-allographer/documents/surrealdb/query_builder.md new file mode 100644 index 0000000..b9a9b65 --- /dev/null +++ b/clients/nim-allographer/documents/surrealdb/query_builder.md @@ -0,0 +1,547 @@ +Example: Query Builder for SurrealDB +=== +> [!WARNING] +> SurrealDB support is planned for v3. The current implementation is for reference only and may not work as expected in v2. + +[back](../../README.md) + +## index + +* [Example: Query Builder for SurrealDB](#example-query-builder-for-surrealdb) + * [index](#index) + * [About SurrealDB](#about-surrealdb) + * [Create Connection](#create-connection) + * [SurrealId type](#surrealid-type) + * [SELECT](#select) + * [get](#get) + * [first](#first) + * [find](#find) + * [columns](#columns) + * [where](#where) + * [fetch](#fetch) + * [INSERT](#insert) + * [insert single row](#insert-single-row) + * [insert rows](#insert-rows) + * [insertId](#insertid) + * [UPDATE](#update) + * [DELETE](#delete) + * [delete all](#delete-all) + * [delete row](#delete-row) + * [Raw Query](#raw-query) + * [Prepared Statement](#prepared-statement) + * [Aggregates](#aggregates) + * [count](#count) + * [max](#max) + * [min](#min) + + + + + + +--- + +## About SurrealDB +[SurrealDB official docs](https://surrealdb.com/docs) +[SurrealDB Github](https://github.com/surrealdb/surrealdb) + +SurrealDB is a next-generation database built on Rust that can handle all type of data structures-relational, document, and graph-and can run in-memory, on a single node, or in a distributed environment. +It's response is JSON and allographer return as `JsonNode`. + +## Create Connection +[to index](#index) + +```nim +import allographer/connection + +let maxConnections = 95 +let timeout = 30 +let surreal = dbOpen(SurrealDb, "test", "test", "user", "pass", "http://surreal", 8000, maxConnections, timeout, true, true).await() +``` + +## SurrealId type + +The `SurrealId` type is used to handle SurrealDB Record IDs. It has the `table name` and `id` data. +`insertId` returns `SurrealId` response and can be used as an argument to the `where` and `find` clauses. + +[Nim API document](https://itsumura-h.github.io/nim-allographer/query_builder/surreal/surreal_types.html#SurrealId) +[SurrealDB Official document](https://surrealdb.com/docs/surrealql/datamodel/ids) + +```nim +let aliceId = surreal.table("user").insertId(%*{"name": "alice"}).await +let aliceOpt = surreal.table("user").where("name", "=", "alice").first().await +let alice = aliceOpt.get() +check aliceId.rawId() == alice["id"].getStr() +check aliceId.table == alice["id"].getStr().split(":")[0] +check $aliceId == alice["id"].getStr().split(":")[1] + +let alice2 = SurrealId.new(aliceId.rawId()) +check alice2.rawId() == alice["id"].getStr() +check alice2.table == alice["id"].getStr().split(":")[0] +check $alice2 == alice["id"].getStr().split(":")[1] + +let alice3 = SurrealId.new(aliceId.table, $aliceId) +check alice3.rawId() == alice["id"].getStr() +check alice3.table == alice["id"].getStr().split(":")[0] +check $alice3 == alice["id"].getStr().split(":")[1] +``` + +## SELECT +[to index](#index) + +https://surrealdb.com/docs/surrealql/statements/select + +When it returns following table + +```sql +INSERT INTO `type` [ + {"bool":true,"string":"alice","int":1,"float":3.14,"datetime":"2023-06-05T11:58:42+00:00"}, + {"bool":false,"string":"bob","int":2,"float":1.11,"datetime":"2023-06-06T11:58:42+00:00"} +] +``` + +|bool|string|int|float|datetime| +|---|---|---|---|---| +|true|alice|1|3.14|2023-06-05T11:58:42+00:00 +|false|bob|2|1.11|2023-06-06T11:58:42+00:00 + + +### get +Retrieving all row from a table + +```nim +import allographer/query_builder + +echo surreal.table("user").get().await +``` + +```nim +DEBUG SELECT * FROM `type` + +@[ + { + "bool":true, + "datetime":"2023-06-05T12:05:07Z", + "float":3.14, + "id":"type:9nxye3dons0juyelv5if", + "int":1, + "string":"alice" + }, + { + "bool":false, + "datetime":"2023-06-06T12:05:07Z" + ,"float":1.11, + "id":"type:tw9iqdycdz9h1egtjkwb", + "int":2, + "string":"bob" + } +] +``` + +### first +Retrieving a single row from a table. This returns `Option[JsonNode]` + +```nim +let res = surreal.table("type").first().await +if res.isSome(): + echo firstRes.get() +``` + +```nim +DEBUG SELECT * FROM `type` LIMIT 1 + +{ + "bool":true, + "datetime":"2023-06-05T12:08:47Z", + "float":3.14, + "id":"type:64t0w6gpnye7tdf083ch", + "int":1, + "string":"alice" +} +``` + +### find +Retrieve a single row by id. This returns `Option[JsonNode]` +Id should be `SurrealId` type. +https://itsumura-h.github.io/nim-allographer/query_builder/surreal/surreal_types.html#SurrealId + +```nim +let id = SurrealId.new("type:64t0w6gpnye7tdf083ch") +let res = surreal.table("type").find(id).await +if res.isSome(): + echo res.get() +``` + +```nim +DEBUG SELECT * FROM `type` WHERE `id` = ? LIMIT 1 ["type:64t0w6gpnye7tdf083ch"] + +{ + "bool":true, + "datetime":"2023-06-05T12:08:47Z", + "float":3.14, + "id":"type:64t0w6gpnye7tdf083ch", + "int":1, + "string":"alice" +} +``` + +### columns +Retrieve columns from table. +```nim +let columns = surreal.table("users").columns().await +columns == @["email", "name", "id"] +``` + + +### where +user +|id|name|email| +|---|---|---| +|user:2jrkvgwpdr02p71sfu1f|alice|alice@example.com| + +```nim +let res = surreal.table("user").where("name", "=", "alice").first().await +if res.isSome(): + echo res.get() +``` + +```nim +{ + "email": "alice@example.com", + "id": "user:iss5w0fp4x3o08t2br3s", + "name": "alice" +} +``` + +### fetch +Fetch and replace records with the remote record data. +It is used for retrieve relational tables. + +auth +|id|name| +|---|---| +|auth:pl93k823yinrm8hzip22|admin| +|auth:e3is4h0txnn6cpmcuoca|editor| + +user +|id|name|email|auth| +|---|---|---|---| +|user:2jrkvgwpdr02p71sfu1f|alice|alice@example.com|auth:pl93k823yinrm8hzip22| + +```nim +let alice = surreal.table("user").where("auth.name", "=", "admin").fetch("auth").first().await +if alice.isSome(): + echo alice.get() +``` + +```nim +{ + "auth": { + "id": "auth:ayp1w74u5oo8m4w2neyi", + "name": "admin" + }, + "email": "alice@example.com", + "id": "user:5d7pgwag7i7uymigdako", + "name": "alice" +} +``` + +## INSERT +[to index](#index) + +https://surrealdb.com/docs/surrealql/statements/insert + +### insert single row +```nim +surreal.table("type").insert(%*{ + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") +}) +.await +``` + +### insert rows +```nim +surreal.table("type").insert(%*[ + { + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") + }, + { + "bool":false, + "string": "bob", + "int": 2, + "float": 1.11, + "datetime": (now() + initDuration(days=1)).format("yyyy-MM-dd'T'HH:mm:sszzz") + } +]) +.await +``` + +### insertId +```nim +let id:SurrealId = surreal.table("type").insertId(%*{ + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") +}) +.await + +echo id.rawId() +``` + +```nim +type:9nxye3dons0juyelv5if +``` + +## UPDATE +[to index](#index) + +https://surrealdb.com/docs/surrealql/statements/update + +```nim +let id = surreal.table("type").insertId(%*{ + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") +}) +.await + +surreal.table("type").where("id", "=", id).update(%*{"string": "bob"}).await +let res = surreal.table("type").find(id).await +if res.isSome(): + echo res.get() +``` + +```nim +{ + "bool":true, + "datetime":"2023-06-06T02:34:25Z", + "float":3.14, + "id":"type:hp0aqct78btlcyr1ho06", + "int":1, + "string":"bob" +} +``` + +## DELETE +[to index](#index) + +https://surrealdb.com/docs/surrealql/statements/delete + +## delete all +```nim +let id = surreal.table("type").insertId(%*{ + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") +}) +.await + +surreal.table("type").delete().await +echo surreal.table("type").get().await +``` + +```nim +@[] +``` + +## delete row +```nim +surreal.table("type").insert(%*[ + { + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") + }, + { + "bool":false, + "string": "bob", + "int": 2, + "float": 1.11, + "datetime": (now() + initDuration(days=1)).format("yyyy-MM-dd'T'HH:mm:sszzz") + } +]) +.await + +let row = surreal.table("type").where("string", "=", "alice").first().await.get() +let rowId = SurrealId.new(row["id"].getStr()) + +surreal.table("type").where("id", "=", rowId).delete().await +echo surreal.table("type").get().await +``` + +```nim +@[ + { + "bool":false, + "datetime":"2023-06-07T03:39:14Z", + "float":"1.11", + "id":"type:y93d6ig6iyrvcez0lc9k", + "int":2, + "string":"bob" + } +] +``` + +## Raw Query +[to index](#INDEX) + +`raw()` returns `RawQuerySurrealDb` type. +You can use `get()`, `first()`, `exec()` and `info()` for raw query. +`get()` returns all rows of result. +`first()` returns first row of result. +`exec()` executes query and not return any variables. +`info()` executes query and returns all data of response. + +```nim +let define = """ +REMOVE TABLE type; +DEFINE TABLE type SCHEMAFULL; +DEFINE FIELD index ON TABLE type TYPE int; +DEFINE INDEX types_index ON TABLE type COLUMNS index UNIQUE; +DEFINE FIELD bool ON TABLE type TYPE bool; +DEFINE FIELD datetime ON TABLE type TYPE datetime; +DEFINE FIELD float ON TABLE type TYPE float; +DEFINE FIELD int ON TABLE type TYPE int; +DEFINE FIELD string ON TABLE type TYPE string; +""" +surreal.raw(define).exec().await() + +echo surreal.raw("INFO FOR TABLE type").info().await + +>> [ + { + "time":"32.762515ms", + "status":"OK", + "result":{ + "ev":{}, + "fd":{ + "bool":"DEFINE FIELD bool ON type TYPE bool", + "datetime":"DEFINE FIELD datetime ON type TYPE datetime", + "float":"DEFINE FIELD float ON type TYPE float", + "index":"DEFINE FIELD index ON type TYPE int", + "int":"DEFINE FIELD int ON type TYPE int", + "string":"DEFINE FIELD string ON type TYPE string" + }, + "ft":{}, + "ix":{ + "types_index":"DEFINE INDEX types_index ON type FIELDS index UNIQUE" + } + } + } +] + +var rows = surreal.raw("SELECT * FROM type").get().await +let max = rows.len + 1 + +surreal.raw( + """ + CREATE type CONTENT { + index: ?, + bool: true, + datetime: ?, + float: 1.11, + int: 1, + string: "aaa" + } + """, + $max, now().format("yyyy-MM-dd'T'HH:mm:sszzz") +) +.exec() +.await + +rows = surreal.raw("SELECT * FROM type").get().await +echo rows + +>> @[ + { + "bool":true, + "datetime":"2023-06-06T04:01:32Z", + "float":1.11, + "id":"type:f1qxioacts8cydbmchpy", + "index":1, + "int":1, + "string":"aaa" + } +] +``` + +## Prepared Statement +[to index](#INDEX) + +SurrealDB の prepared statement は [`documents/surrealdb/prepared_statement.md`](./prepared_statement.md) にまとめています。`prepare()`, `get()`, `first()`, `exec()`, `close()`, `flushStmt(stmt)`, `clearStmtCache()`, `withConn()` が利用できます。 + +```nim +let stmt = surreal.prepare("""SELECT * FROM "type" WHERE "id" = ?""") +let row = await stmt.first(@["type:9nxye3dons0juyelv5if"]) +await stmt.close() +``` + +## Aggregates +[to index](#INDEX) + +### count +```nim +surreal.table("user").insert(%*[ + {"name": "alice", "email": "alice@example.com"}, + {"name": "bob", "email": "bob@example.com"}, + {"name": "charlie", "email": "charlie@example.com"}, +]) +.await + +let count = surreal.table("user").count().await +echo count +``` + +```sh +>> 3 +``` + +### max +```nim +surreal.table("user").insert(%*[ + {"name": "alice", "email": "alice@example.com", "index": 1}, + {"name": "bob", "email": "bob@example.com", "index": 2}, + {"name": "charlie", "email": "charlie@example.com", "index": 3}, +]) +.await + +let max = surreal.table("user").max("index").await +echo max +``` + +``` +>> 3 +``` + +### min +```nim +surreal.table("user").insert(%*[ + {"name": "alice", "email": "alice@example.com", "index": 1}, + {"name": "bob", "email": "bob@example.com", "index": 2}, + {"name": "charlie", "email": "charlie@example.com", "index": 3}, +]) +.await + +let min = surreal.table("user").min("index").await +echo min +``` + +``` +>> 1 +``` diff --git a/clients/nim-allographer/documents/surrealdb/schema_builder.md b/clients/nim-allographer/documents/surrealdb/schema_builder.md new file mode 100644 index 0000000..800c643 --- /dev/null +++ b/clients/nim-allographer/documents/surrealdb/schema_builder.md @@ -0,0 +1,147 @@ +Example: Schema Builder for SurrealDB +=== +> [!WARNING] +> SurrealDB support is planned for v3. The current implementation is for reference only and may not work as expected in v2. + +[back](../../README.md) + +## index + +* [Example: Schema Builder for SurrealDB](#example-schema-builder-for-surrealdb) + * [index](#index) + * [About SurrealDB](#about-surrealdb) + * [Create table](#create-table) + * [Alter Table](#alter-table) + * [add column](#add-column) + * [drop column](#drop-column) + * [drop table](#drop-table) + + + + + + +--- + +## About SurrealDB +[SurrealDB official docs](https://surrealdb.com/docs) +[SurrealDB Github](https://github.com/surrealdb/surrealdb) + +SurrealDB is a next-generation database built on Rust that can handle all type of data structures-relational, document, and graph-and can run in-memory, on a single node, or in a distributed environment. +It's response is JSON and allographer return as `JsonNode`. + +## Create table +You can create `SCHEMAFULL` table for SurrealDB. + +[DEFINE TABLE](https://surrealdb.com/docs/surrealql/statements/define/table) +[DEFINE FIELD](https://surrealdb.com/docs/surrealql/statements/define/field) + +```nim +import std/asyncdispatch +import allographer/connection +import allographer/schema_builder + +let surreal = dbOpen(SurrealDb, "test", "test", "user", "pass", "http://surreal", 8000, 5, 30, false, false).await + +surreal.create( + table("auth", [ + Column.increments("index"), + Column.uuid("uuid"), + Column.string("name"), + Column.timestamps() + ]), + table("user", [ + Column.increments("index"), + Column.string("name"), + Column.foreign("auth").reference("id").onTable("auth").onDelete(SET_NULL) + ]) +) +``` + +These query run. + +```sql +DEFINE TABLE `auth` SCHEMAFULL; +INSERT INTO `_autoincrement_sequences` {table: "auth", column: "index", max_index: 0}; +DEFINE EVENT `autoincrement_auth_index` ON TABLE `auth` WHEN $event = "CREATE" THEN { + LET $val = (SELECT `max_index` FROM `_autoincrement_sequences` WHERE `table` = "auth" AND `column` = "index" LIMIT 1)[0].max_index + 1; + UPDATE `auth` MERGE {index: $val} WHERE id = $after.id; + UPDATE `_autoincrement_sequences` MERGE {max_index: $val} WHERE `table` = "auth" AND `column` = "index"; +}; +DEFINE FIELD `index` ON TABLE `auth` TYPE int; +DEFINE INDEX `auth_index_unique` ON TABLE `auth` COLUMNS `index` UNIQUE; +DEFINE FIELD `uuid` ON TABLE `auth` TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE; +DEFINE INDEX `auth_uuid_unique` ON TABLE `auth` COLUMNS `uuid` UNIQUE; +DEFINE FIELD `name` ON TABLE `auth` TYPE string ASSERT string::len($value) < 255 AND $value != NONE VALUE $value OR ''; +DEFINE FIELD `created_at` ON TABLE `auth` TYPE datetime VALUE $value OR time::now(); +DEFINE INDEX `auth_created_at_index` ON TABLE `auth` COLUMNS `created_at`; +DEFINE FIELD `updated_at` ON TABLE `auth` TYPE datetime VALUE time::now(); +DEFINE INDEX `auth_updated_at_index` ON TABLE `auth` COLUMNS `updated_at`; + +DEFINE TABLE `user` SCHEMAFULL; +INSERT INTO `_autoincrement_sequences` {table: "user", column: "index", max_index: 0}; +DEFINE EVENT `autoincrement_user_index` ON TABLE `user` WHEN $event = "CREATE" THEN { + LET $val = (SELECT `max_index` FROM `_autoincrement_sequences` WHERE `table` = "user" AND `column` = "index" LIMIT 1)[0].max_index + 1; + UPDATE `user` MERGE {index: $val} WHERE id = $after.id; + UPDATE `_autoincrement_sequences` MERGE {max_index: $val} WHERE `table` = "user" AND `column` = "index"; +}; +DEFINE FIELD `index` ON TABLE `user` TYPE int; +DEFINE INDEX `user_index_unique` ON TABLE `user` COLUMNS `index` UNIQUE; +DEFINE FIELD `name` ON TABLE `user` TYPE string ASSERT string::len($value) < 255 AND $value != NONE VALUE $value OR ''; +DEFINE FIELD `auth` ON TABLE `user` TYPE record (`auth`) ASSERT $value != NONE; +``` + + +## Alter Table +### add column +```nim +surreal.alter( + table("auth", [ + Column.increments("index").add(), + Column.string("name").add(), + ]), + table("user", [ + Column.string("email").unique().default("").add(), + Column.foreign("auth").reference("id").onTable("auth").onDelete(SET_NULL).add() + ]) +) +``` + +```sql +INSERT INTO `_autoincrement_sequences` {table: "auth", column: "index", max_index: 0}; +DEFINE EVENT `autoincrement_auth_index` ON TABLE `auth` WHEN $event = "CREATE" THEN { + LET $val = (SELECT `max_index` FROM `_autoincrement_sequences` WHERE `table` = "auth" AND `column` = "index" LIMIT 1)[0].max_index + 1; + UPDATE `auth` MERGE {index: $val} WHERE id = $after.id; + UPDATE `_autoincrement_sequences` MERGE {max_index: $val} WHERE `table` = "auth" AND `column` = "index"; +} +DEFINE FIELD `index` ON TABLE `auth` TYPE int +DEFINE INDEX `auth_index_unique` ON TABLE `auth` COLUMNS `index` UNIQUE +DEFINE FIELD `name` ON TABLE `auth` TYPE string ASSERT string::len($value) < 255 AND $value != NONE VALUE $value OR '' +DEFINE FIELD `email` ON TABLE `user` TYPE string ASSERT string::len($value) < 255 AND $value != NONE VALUE $value OR '' +DEFINE INDEX `user_email_unique` ON TABLE `user` COLUMNS `email` UNIQUE +DEFINE FIELD `auth` ON TABLE `user` TYPE record (`auth`) ASSERT $value != NONE +``` + +### drop column +```nim +surreal.alter( + table("user", [ + Column.dropColumn("name") + ]) +) +``` + +```sql +REMOVE FIELD `name` ON TABLE `user` +``` + +### drop table +```nim +surreal.drop( + table("user") +) +``` + +```sql +REMOVE TABLE `user` +``` diff --git a/clients/nim-allographer/example/.env b/clients/nim-allographer/example/.env new file mode 100644 index 0000000..5f170a1 --- /dev/null +++ b/clients/nim-allographer/example/.env @@ -0,0 +1,22 @@ +DB_SQLITE=true +DB_POSTGRES=true +DB_MARIADB=true +DB_SURREAL=true + +SQLITE_HOST="/root/project/example/db.sqlite3" +# SQLITE_HOST=":memory:" +MY_HOST=mysql +MARIA_HOST=mariadb +MY_PORT=3306 +PG_HOST=postgres +PG_PORT=5432 +DB_USER=user +DB_PASSWORD=pass +DB_DATABASE=database +DB_MAX_CONNECTION=95 +DB_TIMEOUT=30 + +# Logging +LOG_IS_DISPLAY=true +LOG_IS_FILE=false +LOG_DIR="/root/project/example/logs" diff --git a/clients/nim-allographer/example/columnType.nim b/clients/nim-allographer/example/columnType.nim new file mode 100644 index 0000000..26e1c23 --- /dev/null +++ b/clients/nim-allographer/example/columnType.nim @@ -0,0 +1,58 @@ +# nim c -r -d:reset example/columnType.nim +import std/json +import std/asyncdispatch +import ../src/allographer/query_builder +import ../src/allographer/schema_builder +import ./connections + +asyncBlock: + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("users",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("password").nullable(), + Column.string("address").nullable(), + Column.date("birth_date").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]), + table("sample", [ + Column.increments("id"), + Column.float("float"), + Column.string("string"), + Column.datetime("datetime"), + Column.string("null").nullable(), + Column.boolean("is_admin") + ]) + ]) + + echo await rdb.select("id", "name", "address").table("users") + .limit(2) + .get() + + echo await rdb.select("id", "name", "address").table("users").first() + + echo await rdb.select("id", "name", "address").table("users").find(3) + + await rdb.table("sample").insert(%*{ + "id": 1, + "float": 3.14, + "string": "string", + "datetime": "2019-01-01 12:00:00.1234", + "is_admin": true + }) + + echo await rdb.select("id", "float", "string", "datetime", "null", "is_admin").table("sample").get() + + echo await rdb.select("id", "float", "string", "datetime", "null", "is_admin").table("sample").get() + + + var sql = "update users set name='John' where id = 1" + await rdb.raw(sql).exec() + + sql = "select * from users where id = 1" + echo await rdb.raw(sql).get() diff --git a/clients/nim-allographer/example/connections.nim b/clients/nim-allographer/example/connections.nim new file mode 100644 index 0000000..c245b8e --- /dev/null +++ b/clients/nim-allographer/example/connections.nim @@ -0,0 +1,21 @@ +import std/strutils +import ../src/allographer/connection + +let + maxConnections = 95 + timeout = 30 + mysqlUrl = "mysql://user:pass@mysql:3306/database" + mariadbUrl = "mariadb://user:pass@mariadb:3306/database" + pgUrl = "postgresql://user:pass@postgres:5432/database" + + # rdb* = dbOpen(SQLite3, sqliteHost, maxConnections=maxConnections, shouldDisplayLog=false) + rdb* = dbOpen(SQLite3, ":memory:", maxConnections=maxConnections, shouldDisplayLog=false) + # rdb* = dbOpen(MySQL, mysqlUrl, maxConnections, timeout, shouldDisplayLog=true) + # rdb* = dbOpen(MariaDB, mariadbUrl, maxConnections, timeout, shouldDisplayLog=true) + # rdb* = dbOpen(PostgreSQL, pgUrl, maxConnections, timeout, shouldDisplayLog=true) + +template asyncBlock*(body:untyped) = + (proc(){.async.}= + body + )() + .waitFor() diff --git a/clients/nim-allographer/example/database/.env b/clients/nim-allographer/example/database/.env new file mode 100644 index 0000000..2126fe5 --- /dev/null +++ b/clients/nim-allographer/example/database/.env @@ -0,0 +1,12 @@ +SQLITE_HOST="/root/project/example/db.sqlite3" +# SQLITE_HOST=":memory:" +PG_URL="postgresql://user:pass@postgres:5432/database" +MYSQL_URL="mysql://user:pass@mysql:3306/database" +MARIA_URL="mariadb://user:pass@mariadb:3306/database" +DB_MAX_CONNECTION=95 +DB_TIMEOUT=30 + +# Logging +LOG_IS_DISPLAY=true +LOG_IS_FILE=false +LOG_DIR="/root/project/example/logs" diff --git a/clients/nim-allographer/example/database/connection.nim b/clients/nim-allographer/example/database/connection.nim new file mode 100644 index 0000000..a87d813 --- /dev/null +++ b/clients/nim-allographer/example/database/connection.nim @@ -0,0 +1,17 @@ +import std/asyncdispatch +import std/os +import std/strutils +import ../../src/allographer/connection + +let + pgUrl = getEnv("PG_URL") + mariaUrl = getEnv("MARIA_URL") + mysqlUrl = getEnv("MYSQL_URL") + maxConnections = getEnv("DB_MAX_CONNECTION").parseInt + timeout = getEnv("DB_TIMEOUT").parseInt + +# let rdb* = dbOpen(SQLite3, "./db.sqlite3", shouldDisplayLog=true) +let rdb* = dbOpen(PostgreSQL, pgUrl, maxConnections, timeout, shouldDisplayLog=true) +# let rdb* = dbOpen(Mariadb, mariaUrl, maxConnections, timeout, shouldDisplayLog=true) +# let rdb* = dbOpen(MySQL, mysqlUrl, maxConnections, timeout, shouldDisplayLog=true) +# let rdb* = dbOpen(SurrealDB, "test", "test", "user", "pass", "http://surreal", 8000, 5, 30, shouldDisplayLog=true).waitFor() diff --git a/clients/nim-allographer/example/database/develop.sh b/clients/nim-allographer/example/database/develop.sh new file mode 100755 index 0000000..ebaefb9 --- /dev/null +++ b/clients/nim-allographer/example/database/develop.sh @@ -0,0 +1,5 @@ +nim c -d:reset --threads:off ./migrations/migrate.nim +nim c --threads:off ./seeder/develop + +./migrations/migrate +./seeder/develop diff --git a/clients/nim-allographer/example/database/migrations/create_all_type_table.nim b/clients/nim-allographer/example/database/migrations/create_all_type_table.nim new file mode 100644 index 0000000..b0b1dff --- /dev/null +++ b/clients/nim-allographer/example/database/migrations/create_all_type_table.nim @@ -0,0 +1,42 @@ +import std/json +import ../../../src/allographer/schema_builder +import ../connection + +proc createAllTypeTable*() = + ## 0002 + rdb.create([ + table("IntRelation", [ + Column.increments("id") + ]), + table("StrRelation", [ + Column.uuid("uuid") + ]), + table("Types", [ + Column.increments("id"), + Column.integer("integer"), + Column.smallInteger("smallInteger"), + Column.mediumInteger("mediumInteger"), + Column.bigInteger("bigInteger"), + Column.decimal("decimal", 10, 3), + Column.double("double", 10, 3), + Column.float("float"), + Column.uuid("uuid"), + Column.char("char", 255), + Column.string("string"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date"), + Column.datetime("datetime"), + Column.time("time"), + Column.timestamp("timestamp"), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean"), + Column.enumField("enumField", ["A", "B", "C"]), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]), + ]) diff --git a/clients/nim-allographer/example/database/migrations/init_databaase.nim b/clients/nim-allographer/example/database/migrations/init_databaase.nim new file mode 100644 index 0000000..08c1b12 --- /dev/null +++ b/clients/nim-allographer/example/database/migrations/init_databaase.nim @@ -0,0 +1,23 @@ +import ../../../src/allographer/schema_builder +import ../connection + +proc initDatabaase*() = + ## 0001 + rdb.create([ + table("user", [ + Column.uuid("id").index().comment("User ID"), + Column.string("name").comment("User name"), + Column.string("email").comment("User email address"), + Column.string("password").comment("User password"), + Column.integer("created_at").index().comment("Created at"), + Column.integer("updated_at").index().comment("Updated at"), + ], "User table"), + table("post", [ + Column.uuid("id").index().comment("Post ID"), + Column.string("title").comment("Post title"), + Column.string("content").comment("Post content"), + Column.strForeign("user_id").reference("id").onTable("user").comment("User ID"), + Column.integer("created_at").index().comment("Created at"), + Column.integer("updated_at").index().comment("Updated at"), + ], "Post table"), + ]) diff --git a/clients/nim-allographer/example/database/migrations/migrate.nim b/clients/nim-allographer/example/database/migrations/migrate.nim new file mode 100644 index 0000000..f1f4909 --- /dev/null +++ b/clients/nim-allographer/example/database/migrations/migrate.nim @@ -0,0 +1,12 @@ +import std/asyncdispatch +import ../../../src/allographer/schema_builder +import ../connection +import ./init_databaase +import ./create_all_type_table + +proc migrate*() = + initDatabaase() + createAllTypeTable() + +migrate() +createSchema(rdb).waitFor() diff --git a/clients/nim-allographer/example/database/production.sh b/clients/nim-allographer/example/database/production.sh new file mode 100755 index 0000000..abd787f --- /dev/null +++ b/clients/nim-allographer/example/database/production.sh @@ -0,0 +1,5 @@ +nim c --threads:off ./migrations/migrate.nim +nim c --threads:off database/seeder/production + +./migrations/migrate +APP_ENV=production ./database/seeder/production diff --git a/clients/nim-allographer/example/database/schema.nim b/clients/nim-allographer/example/database/schema.nim new file mode 100644 index 0000000..cbda0c4 --- /dev/null +++ b/clients/nim-allographer/example/database/schema.nim @@ -0,0 +1,67 @@ +import std/json + +type IntRelationTable* = object + ## IntRelation + id*: int + + +type StrRelationTable* = object + ## StrRelation + uuid*: string + + +type UserTable* = object + ## user + id*: string + name*: string + email*: string + password*: string + created_at*: int + updated_at*: int + + +type AuthTable* = object + ## Auth + id*: int + auth*: string + + +type TypesTable* = object + ## Types + id*: int + integer*: int + smallInteger*: int + mediumInteger*: int + bigInteger*: int + decimal*: float + double*: float + float*: float + uuid*: string + char*: string + string*: string + text*: string + mediumText*: string + longText*: string + date*: string + datetime*: string + time*: string + timestamp*: string + created_at*: string + updated_at*: string + deleted_at*: string + binary*: string + boolean*: bool + enumField*: string + json*: JsonNode + int_relation_id*: int + str_relation_id*: string + + +type PostTable* = object + ## post + id*: string + title*: string + content*: string + user_id*: string + created_at*: int + updated_at*: int diff --git a/clients/nim-allographer/example/database/seeder/data/post_seeder.nim b/clients/nim-allographer/example/database/seeder/data/post_seeder.nim new file mode 100644 index 0000000..eac5d2f --- /dev/null +++ b/clients/nim-allographer/example/database/seeder/data/post_seeder.nim @@ -0,0 +1,32 @@ +import std/json +import std/strformat +import std/asyncdispatch +import std/oids +import std/times +import ../../../../src/allographer/query_builder +import ../../connection +import ../../schema + +proc postSeeder*() {.async.} = + seeder(rdb, "post"): + let postCount = rdb.table("post").count().await + if postCount > 0: + return + + let users = rdb.table("user").get().orm(UserTable).await + if users.len == 0: + raise newException(ValueError, "No users found in database") + + var postList: seq[JsonNode] + for i in 1..users.len: + let row = PostTable( + id: $genOid(), + title: &"post {i}", + content: &"content {i}", + user_id: users[i-1].id, + created_at: now().toTime().toUnix().int, + updated_at: now().toTime().toUnix().int + ) + postList.add(%row) + + rdb.table("post").insert(postList).await diff --git a/clients/nim-allographer/example/database/seeder/data/user_seeder.nim b/clients/nim-allographer/example/database/seeder/data/user_seeder.nim new file mode 100644 index 0000000..0aa346d --- /dev/null +++ b/clients/nim-allographer/example/database/seeder/data/user_seeder.nim @@ -0,0 +1,24 @@ +import std/json +import std/strformat +import std/asyncdispatch +import std/oids +import std/times +import ../../../../src/allographer/query_builder +import ../../connection +import ../../schema + +proc userSeeder*() {.async.} = + seeder(rdb, "user"): + var userList: seq[JsonNode] + for i in 1..10: + let row = UserTable( + id: $genOid(), + name: &"user {i}", + email: &"user{i}@example.com", + password: &"password{i}", + created_at: now().toTime().toUnix().int, + updated_at: now().toTime().toUnix().int, + ) + userList.add(%row) + + rdb.table("user").insert(userList).await diff --git a/clients/nim-allographer/example/database/seeder/develop.nim b/clients/nim-allographer/example/database/seeder/develop.nim new file mode 100644 index 0000000..5d589f1 --- /dev/null +++ b/clients/nim-allographer/example/database/seeder/develop.nim @@ -0,0 +1,16 @@ +import std/asyncdispatch +import std/os +import ./data/user_seeder +import ./data/post_seeder + + +proc seed() {.async.} = + let env = getEnv("APP_ENV") + if env != "develop": + raise newException(CatchableError, "This command is only available in the develop environment") + + userSeeder().await + postSeeder().await + + +seed().waitFor() diff --git a/clients/nim-allographer/example/database/seeder/production.nim b/clients/nim-allographer/example/database/seeder/production.nim new file mode 100644 index 0000000..1b0e7a2 --- /dev/null +++ b/clients/nim-allographer/example/database/seeder/production.nim @@ -0,0 +1,14 @@ +import std/asyncdispatch +import std/os +import ./data/user_seeder +import ./data/post_seeder + +proc seed() = + let env = getEnv("APP_ENV") + if env != "production": + raise newException(Exception, "This command is only available in the production environment") + + userSeeder().waitFor() + postSeeder().waitFor() + +seed() diff --git a/clients/nim-allographer/example/database/seeder/staging.nim b/clients/nim-allographer/example/database/seeder/staging.nim new file mode 100644 index 0000000..e59d059 --- /dev/null +++ b/clients/nim-allographer/example/database/seeder/staging.nim @@ -0,0 +1,14 @@ +import std/asyncdispatch +import std/os +import ./data/user_seeder +import ./data/post_seeder + +proc seed() = + let env = getEnv("APP_ENV") + if env != "staging": + raise newException(Exception, "This command is only available in the staging environment") + + userSeeder().waitFor() + postSeeder().waitFor() + +seed() diff --git a/clients/nim-allographer/example/database/staging.sh b/clients/nim-allographer/example/database/staging.sh new file mode 100755 index 0000000..61f5866 --- /dev/null +++ b/clients/nim-allographer/example/database/staging.sh @@ -0,0 +1,5 @@ +nim c -d:reset --threads:off./migrations/migrate.nim +nim c --threads:off database/seeder/staging + +./migrations/migrate +APP_ENV=staging ./database/seeder/staging diff --git a/clients/nim-allographer/example/ddd/repository.nim b/clients/nim-allographer/example/ddd/repository.nim new file mode 100644 index 0000000..9cfdafb --- /dev/null +++ b/clients/nim-allographer/example/ddd/repository.nim @@ -0,0 +1,17 @@ +import json +import std/asyncdispatch +import std/options +import ../../src/allographer/query_builder +import ../connections + +type Repository* = ref object + rdb*: SqliteConnections + +proc newRepository*(): Repository = + Repository(rdb: rdb) + +proc getUsers*(this: Repository): seq[JsonNode] = + this.rdb.table("users").get().waitFor() + +proc getUser*(this: Repository, id: int): Option[JsonNode] = + this.rdb.table("users").find(id).waitFor() diff --git a/clients/nim-allographer/example/ddd/service.nim b/clients/nim-allographer/example/ddd/service.nim new file mode 100644 index 0000000..d939a66 --- /dev/null +++ b/clients/nim-allographer/example/ddd/service.nim @@ -0,0 +1,18 @@ +import json +import std/options +import ../../src/allographer/query_builder +import repository + +type Service = ref object + repository:Repository + +proc newService*():Service = + return Service( + repository: newRepository() + ) + +proc getUsers*(this:Service):seq[JsonNode] = + return this.repository.getUsers() + +proc getUser*(this:Service, id:int):Option[JsonNode] = + return this.repository.getUser(id) diff --git a/clients/nim-allographer/example/mariadb/benchmark.nim b/clients/nim-allographer/example/mariadb/benchmark.nim new file mode 100644 index 0000000..593e81a --- /dev/null +++ b/clients/nim-allographer/example/mariadb/benchmark.nim @@ -0,0 +1,126 @@ +# nim c -r --threads:off -d:reset example/mariadb/benchmark.nim + +import std/asyncdispatch +import std/json +import std/os +import std/random +import std/strutils +import std/monotimes +import std/times +import ../../src/allographer/connection +import ../../src/allographer/query_builder +import ../../src/allographer/schema_builder + + +randomize() + +const + range1_10000 = 1..10000 + countNum = 500 + shouldDisplayLog = false + selectSql = "SELECT `index` as id, `randomNumber` FROM `World` WHERE `index` = ?" + updateSql = "UPDATE `World` SET `randomNumber` = ? WHERE `index` = ?" + + +let + maxConnections = getEnv("DB_MAX_CONNECTION", "95").parseInt + timeout = getEnv("DB_TIMEOUT", "30").parseInt + + mariaUrl = getEnv("MARIA_URL") + + +proc timeProcess(name: string, cb: proc(): Future[void]) {.async.} = + var eachTime = 0.0 + var sumTime = 0.0 + const repeatCount = 5 + var resultStr = "" + + for i in 1..repeatCount: + sleep(100) + let start = getMonoTime() + await cb() + eachTime = float64((getMonoTime() - start).inMilliseconds) / 1000.0 + sumTime += eachTime + if i > 1: resultStr.add("\n") + resultStr.add("|" & $i & "|" & $eachTime & "|") + + echo name + echo "|num|time|" + echo "|---|---|" + echo resultStr + echo "|Avg|" & $(sumTime / repeatCount) & "|" + echo "" + + +proc mariaBenchmarkScenario(rdb: MariadbConnections) {.async.} = + rdb.create( + table("World", [ + Column.increments("index"), + Column.integer("randomNumber").default(0) + ]) + ) + seeder(rdb, "World"): + var data = newSeq[JsonNode]() + for i in range1_10000: + data.add( + %*{"randomNumber": rand(range1_10000)} + ) + await rdb.table("World").insert(data) + + proc benchUpdate() {.async.} = + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + discard rdb.select("index as id", "randomNumber").table("World").where("index", "=", index).first().await + rdb.table("World").where("index", "=", index).update(%*{"randomNumber": number}).await + )() + await all(futures) + + proc benchUpdatePreparedCold() {.async.} = + let selectStmt = rdb.prepare(selectSql) + let updateStmt = rdb.prepare(updateSql) + defer: + selectStmt.close().await + updateStmt.close().await + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + rdb.withConn( + proc(ctx: MariadbPreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @[$index]) + await updateStmt.exec(ctx, @[$number, $index]) + ) + )() + await all(futures) + + let selectStmtWarm = rdb.prepare(selectSql) + let updateStmtWarm = rdb.prepare(updateSql) + + proc benchUpdatePreparedWarm() {.async.} = + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + rdb.withConn( + proc(ctx: MariadbPreparedContext): Future[void] {.async.} = + discard await selectStmtWarm.first(ctx, @[$index]) + await updateStmtWarm.exec(ctx, @[$number, $index]) + ) + )() + await all(futures) + + await timeProcess("mariadb benchmark", benchUpdate) + await timeProcess("mariadb benchmark prepared cold", benchUpdatePreparedCold) + await timeProcess("mariadb benchmark prepared warm", benchUpdatePreparedWarm) + + +proc main() = + let rdb = dbOpen(MariaDB, mariaUrl, maxConnections, timeout, shouldDisplayLog=shouldDisplayLog) + mariaBenchmarkScenario(rdb).waitFor + +main() diff --git a/clients/nim-allographer/example/mariadb/test.c b/clients/nim-allographer/example/mariadb/test.c new file mode 100644 index 0000000..c25f41f --- /dev/null +++ b/clients/nim-allographer/example/mariadb/test.c @@ -0,0 +1,164 @@ +// apt install -y libcurl4-openssl-dev +// gcc test.c -o test -lmariadb -lcurl && ./test + +#include +#include +#include +#include +#include + +struct MemoryStruct { + char *memory; + size_t size; +}; + +static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + mem->memory = realloc(mem->memory, mem->size + realsize + 1); + if (mem->memory == NULL) { + fprintf(stderr, "Not enough memory (realloc returned NULL)\n"); + exit(EXIT_FAILURE); + } + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +int main() { + MYSQL *conn; + MYSQL_STMT *stmt; + MYSQL_BIND bind[5]; + my_bool is_null[5] = {0}; + unsigned long length[5]; + + // MariaDBに接続するための設定 + const char *host = "mariadb"; + const char *user = "user"; + const char *password = "pass"; + const char *database = "database"; + + conn = mysql_init(NULL); + if (conn == NULL) { + fprintf(stderr, "mysql_init() failed\n"); + exit(EXIT_FAILURE); + } + + if (mysql_real_connect(conn, host, user, password, database, 0, NULL, 0) == NULL) { + fprintf(stderr, "mysql_real_connect() failed\n"); + mysql_close(conn); + exit(EXIT_FAILURE); + } + + // テーブルを削除 + if (mysql_query(conn, "DROP TABLE IF EXISTS `test`")) { + fprintf(stderr, "DROP TABLE failed. Error: %s\n", mysql_error(conn)); + exit(EXIT_FAILURE); + } + + // テーブルを作成 + if (mysql_query(conn, "CREATE TABLE IF NOT EXISTS `test` (" + "`bool` BOOLEAN," + "`int` INT," + "`float` DOUBLE," + "`str` VARCHAR(255)," + "`data` BLOB)")) { + fprintf(stderr, "CREATE TABLE failed. Error: %s\n", mysql_error(conn)); + exit(EXIT_FAILURE); + } + + CURL *curl; + CURLcode res; + struct MemoryStruct chunk; + chunk.memory = malloc(1); + chunk.size = 0; + + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://nim-lang.org/assets/img/twitter_banner.png"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + exit(EXIT_FAILURE); + } + + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + + // データを挿入 + stmt = mysql_stmt_init(conn); + if (!stmt) { + fprintf(stderr, "mysql_stmt_init() failed\n"); + exit(EXIT_FAILURE); + } + + const char *insert_query = "INSERT INTO `test` (`bool`, `int`, `float`, `str`, `data`) VALUES (?, ?, ?, ?, ?)"; + if (mysql_stmt_prepare(stmt, insert_query, strlen(insert_query))) { + fprintf(stderr, "mysql_stmt_prepare() failed. Error: %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + + memset(bind, 0, sizeof(bind)); + + my_bool bool_value = 1; + int int_value = 1; + double float_value = 1.1; + char str_value[255] = "alice"; + length[0] = sizeof(bool_value); + length[1] = sizeof(int_value); + length[2] = sizeof(float_value); + length[3] = strlen(str_value); + length[4] = chunk.size; + + bind[0].buffer_type = MYSQL_TYPE_TINY; + bind[0].buffer = (char *)&bool_value; + bind[0].is_null = &is_null[0]; + bind[0].length = &length[0]; + + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = (char *)&int_value; + bind[1].is_null = &is_null[1]; + bind[1].length = &length[1]; + + bind[2].buffer_type = MYSQL_TYPE_DOUBLE; + bind[2].buffer = (char *)&float_value; + bind[2].is_null = &is_null[2]; + bind[2].length = &length[2]; + + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = (char *)str_value; + bind[3].buffer_length = sizeof(str_value); + bind[3].is_null = &is_null[3]; + bind[3].length = &length[3]; + + bind[4].buffer_type = MYSQL_TYPE_BLOB; + bind[4].buffer = (char *)chunk.memory; + bind[4].buffer_length = chunk.size; + bind[4].is_null = &is_null[4]; + bind[4].length = &length[4]; + + if (mysql_stmt_bind_param(stmt, bind)) { + fprintf(stderr, "mysql_stmt_bind_param() failed. Error: %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + + if (mysql_stmt_execute(stmt)) { + fprintf(stderr, "mysql_stmt_execute() failed. Error: %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + + mysql_stmt_close(stmt); + mysql_close(conn); + free(chunk.memory); + + return 0; +} diff --git a/clients/nim-allographer/example/migrate.nim b/clients/nim-allographer/example/migrate.nim new file mode 100644 index 0000000..39a454f --- /dev/null +++ b/clients/nim-allographer/example/migrate.nim @@ -0,0 +1,41 @@ +import json +import ../src/allographer/schema_builder +import connections + + +# migration +rdb.create([ + table("Auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("Users",[ + Column.increments("id"), + Column.uuid("uuid"), + Column.string("oid").index().nullable(), + Column.string("oid2").index().nullable(), + Column.string("Name").nullable(), + Column.date("birth_date").nullable(), + Column.foreign("auth_id").reference("id").onTable("Auth").onDelete(SET_NULL).default(1) + ]), +]) + +rdb.alter([ + table("Users", [ + # add column + Column.string("aaa").default("").add(), + Column.foreign("bbb").reference("id").onTable("Auth").onDelete(SET_NULL).add(), + # change column definition + Column.string("aaa").nullable().change(), + # change column name + Column.renameColumn("aaa", "ccc"), + # delete column + Column.dropColumn("ccc"), + Column.dropColumn("bbb"), + ]), + # change table name + table("Users").renameTo("members"), +]) + +# drop table +rdb.drop(table("members")) diff --git a/clients/nim-allographer/example/mysql/test.c b/clients/nim-allographer/example/mysql/test.c new file mode 100644 index 0000000..c25f41f --- /dev/null +++ b/clients/nim-allographer/example/mysql/test.c @@ -0,0 +1,164 @@ +// apt install -y libcurl4-openssl-dev +// gcc test.c -o test -lmariadb -lcurl && ./test + +#include +#include +#include +#include +#include + +struct MemoryStruct { + char *memory; + size_t size; +}; + +static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + mem->memory = realloc(mem->memory, mem->size + realsize + 1); + if (mem->memory == NULL) { + fprintf(stderr, "Not enough memory (realloc returned NULL)\n"); + exit(EXIT_FAILURE); + } + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +int main() { + MYSQL *conn; + MYSQL_STMT *stmt; + MYSQL_BIND bind[5]; + my_bool is_null[5] = {0}; + unsigned long length[5]; + + // MariaDBに接続するための設定 + const char *host = "mariadb"; + const char *user = "user"; + const char *password = "pass"; + const char *database = "database"; + + conn = mysql_init(NULL); + if (conn == NULL) { + fprintf(stderr, "mysql_init() failed\n"); + exit(EXIT_FAILURE); + } + + if (mysql_real_connect(conn, host, user, password, database, 0, NULL, 0) == NULL) { + fprintf(stderr, "mysql_real_connect() failed\n"); + mysql_close(conn); + exit(EXIT_FAILURE); + } + + // テーブルを削除 + if (mysql_query(conn, "DROP TABLE IF EXISTS `test`")) { + fprintf(stderr, "DROP TABLE failed. Error: %s\n", mysql_error(conn)); + exit(EXIT_FAILURE); + } + + // テーブルを作成 + if (mysql_query(conn, "CREATE TABLE IF NOT EXISTS `test` (" + "`bool` BOOLEAN," + "`int` INT," + "`float` DOUBLE," + "`str` VARCHAR(255)," + "`data` BLOB)")) { + fprintf(stderr, "CREATE TABLE failed. Error: %s\n", mysql_error(conn)); + exit(EXIT_FAILURE); + } + + CURL *curl; + CURLcode res; + struct MemoryStruct chunk; + chunk.memory = malloc(1); + chunk.size = 0; + + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://nim-lang.org/assets/img/twitter_banner.png"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + exit(EXIT_FAILURE); + } + + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + + // データを挿入 + stmt = mysql_stmt_init(conn); + if (!stmt) { + fprintf(stderr, "mysql_stmt_init() failed\n"); + exit(EXIT_FAILURE); + } + + const char *insert_query = "INSERT INTO `test` (`bool`, `int`, `float`, `str`, `data`) VALUES (?, ?, ?, ?, ?)"; + if (mysql_stmt_prepare(stmt, insert_query, strlen(insert_query))) { + fprintf(stderr, "mysql_stmt_prepare() failed. Error: %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + + memset(bind, 0, sizeof(bind)); + + my_bool bool_value = 1; + int int_value = 1; + double float_value = 1.1; + char str_value[255] = "alice"; + length[0] = sizeof(bool_value); + length[1] = sizeof(int_value); + length[2] = sizeof(float_value); + length[3] = strlen(str_value); + length[4] = chunk.size; + + bind[0].buffer_type = MYSQL_TYPE_TINY; + bind[0].buffer = (char *)&bool_value; + bind[0].is_null = &is_null[0]; + bind[0].length = &length[0]; + + bind[1].buffer_type = MYSQL_TYPE_LONG; + bind[1].buffer = (char *)&int_value; + bind[1].is_null = &is_null[1]; + bind[1].length = &length[1]; + + bind[2].buffer_type = MYSQL_TYPE_DOUBLE; + bind[2].buffer = (char *)&float_value; + bind[2].is_null = &is_null[2]; + bind[2].length = &length[2]; + + bind[3].buffer_type = MYSQL_TYPE_STRING; + bind[3].buffer = (char *)str_value; + bind[3].buffer_length = sizeof(str_value); + bind[3].is_null = &is_null[3]; + bind[3].length = &length[3]; + + bind[4].buffer_type = MYSQL_TYPE_BLOB; + bind[4].buffer = (char *)chunk.memory; + bind[4].buffer_length = chunk.size; + bind[4].is_null = &is_null[4]; + bind[4].length = &length[4]; + + if (mysql_stmt_bind_param(stmt, bind)) { + fprintf(stderr, "mysql_stmt_bind_param() failed. Error: %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + + if (mysql_stmt_execute(stmt)) { + fprintf(stderr, "mysql_stmt_execute() failed. Error: %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + + mysql_stmt_close(stmt); + mysql_close(conn); + free(chunk.memory); + + return 0; +} diff --git a/clients/nim-allographer/example/postgres/benchmark.nim b/clients/nim-allographer/example/postgres/benchmark.nim new file mode 100644 index 0000000..f618a38 --- /dev/null +++ b/clients/nim-allographer/example/postgres/benchmark.nim @@ -0,0 +1,123 @@ +# nim c -r --threads:off -d:reset example/postgres/benchmark.nim + +import std/asyncdispatch +import std/json +import std/os +import std/random +import std/strutils +import std/monotimes +import std/times +import ../../src/allographer/connection +import ../../src/allographer/query_builder +import ../../src/allographer/schema_builder + + +randomize() + +const + range1_10000 = 1..10000 + countNum = 500 + shouldDisplayLog = false + pgUrl = "postgresql://user:pass@postgres:5432/database" + selectSql = """SELECT "index" as id, "randomNumber" FROM "World" WHERE "index" = ?""" + updateSql = """UPDATE "World" SET "randomNumber" = ? WHERE "index" = ?""" + maxConnections = 95 + timeout = 30 + +let rdb = dbOpen(PostgreSQL, pgUrl, maxConnections, timeout, shouldDisplayLog=shouldDisplayLog) + + +proc timeProcess(name: string, cb: proc(): Future[void]) {.async.} = + var eachTime = 0.0 + var sumTime = 0.0 + const repeatCount = 5 + var resultStr = "" + + for i in 1..repeatCount: + sleep(100) + let start = getMonoTime() + await cb() + eachTime = float64((getMonoTime() - start).inMilliseconds) / 1000.0 + sumTime += eachTime + if i > 1: resultStr.add("\n") + resultStr.add("|" & $i & "|" & $eachTime & "|") + + echo name + echo "|num|time|" + echo "|---|---|" + echo resultStr + echo "|Avg|" & $(sumTime / repeatCount) & "|" + echo "" + + +rdb.create( + table("World", [ + Column.increments("index"), + Column.integer("randomNumber").default(0) + ]) +) +seeder(rdb, "World"): + var data = newSeq[JsonNode]() + for i in range1_10000: + data.add( + %*{"randomNumber": rand(range1_10000)} + ) + rdb.table("World").insert(data).waitFor + + +proc benchUpdate() {.async.} = + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + discard rdb.select("index as id", "randomNumber").table("World").where("index", "=", index).first().await + rdb.table("World").where("index", "=", index).update(%*{"randomNumber": number}).await + )() + await all(futures) + + +proc benchUpdatePreparedCold() {.async.} = + let selectStmt = rdb.prepare(selectSql) + let updateStmt = rdb.prepare(updateSql) + defer: + selectStmt.close().await + updateStmt.close().await + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + rdb.withConn( + proc(ctx: PostgresPreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @[$index]) + await updateStmt.exec(ctx, @[$number, $index]) + ) + )() + await all(futures) + + +let selectStmtWarm = rdb.prepare(selectSql) +let updateStmtWarm = rdb.prepare(updateSql) + +proc benchUpdatePreparedWarm() {.async.} = + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + rdb.withConn( + proc(ctx: PostgresPreparedContext): Future[void] {.async.} = + discard await selectStmtWarm.first(ctx, @[$index]) + await updateStmtWarm.exec(ctx, @[$number, $index]) + ) + )() + await all(futures) + + +proc main(){.async.} = + await timeProcess("postgres benchmark", benchUpdate) + await timeProcess("postgres benchmark prepared cold", benchUpdatePreparedCold) + await timeProcess("postgres benchmark prepared warm", benchUpdatePreparedWarm) + +main().waitFor diff --git a/clients/nim-allographer/example/postgres/test.c b/clients/nim-allographer/example/postgres/test.c new file mode 100644 index 0000000..fd07db4 --- /dev/null +++ b/clients/nim-allographer/example/postgres/test.c @@ -0,0 +1,147 @@ +// gcc -I/usr/include/postgresql -I/usr/local/include/curl -o test test.c -lpq -lcurl && ./test + +#include +#include +#include +#include +#include + +struct MemoryStruct +{ + unsigned char *memory; + size_t size; +}; + +static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + mem->memory = realloc(mem->memory, mem->size + realsize); + if (mem->memory == NULL) + { + /* out of memory! */ + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + + return realsize; +} + +void exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} + +int main() +{ + + // ################################################## + // # Binary image + // ################################################## + + CURL *curl_handle; + CURLcode curlRes; + + struct MemoryStruct chunk; + + chunk.memory = malloc(1); /* will be grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ + + curl_global_init(CURL_GLOBAL_ALL); + + /* init the curl session */ + curl_handle = curl_easy_init(); + + /* specify URL to get */ + curl_easy_setopt(curl_handle, CURLOPT_URL, "https://nim-lang.org/assets/img/twitter_banner.png"); + + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + + /* some servers don't like requests that are made without a user-agent + field, so we provide one */ + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + /* get it! */ + curlRes = curl_easy_perform(curl_handle); + + /* check for errors */ + if (curlRes != CURLE_OK) + { + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(curlRes)); + } + else + { + printf("%lu bytes retrieved\n", (long)chunk.size); + } + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + /* we're done with libcurl, so clean it up */ + curl_global_cleanup(); + + // ################################################## + // # Postgres + // ################################################## + const char *conninfo; + PGconn *conn; + PGresult *res; + + // Specify your connection info + conninfo = "dbname=database user=user password=pass hostaddr=postgres port=5432"; + + // Make a connection to the database + // conn = PQconnectdb(conninfo); + conn = PQsetdbLogin("postgres", "5432", NULL, NULL, "database", "user", "pass"); + + // Check to see that the backend connection was successfully made + if (PQstatus(conn) != CONNECTION_OK) + { + fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn)); + exit_nicely(conn); + } + + // Your binary data + // unsigned char binary_data[] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21}; + unsigned char binary_data[chunk.size]; + memcpy(binary_data, chunk.memory, chunk.size); + int binary_data_size = sizeof(binary_data) / sizeof(binary_data[0]); + + // INSERT query + const char *query = "INSERT INTO image (data) VALUES ($1)"; + + const char *paramValues[1]; + int paramLengths[1]; + int paramFormats[1]; + + paramValues[0] = (char *)binary_data; + paramLengths[0] = binary_data_size; + paramFormats[0] = 1; // binary + + // Execute the INSERT statement + res = PQexecParams(conn, query, 1, NULL, paramValues, paramLengths, paramFormats, 1); + + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + fprintf(stderr, "INSERT command failed: %s", PQerrorMessage(conn)); + PQclear(res); + exit_nicely(conn); + } + + // Clear result + PQclear(res); + + // Close connection + PQfinish(conn); + + return 0; +} diff --git a/clients/nim-allographer/example/prepare.nim b/clients/nim-allographer/example/prepare.nim new file mode 100644 index 0000000..ba182d7 --- /dev/null +++ b/clients/nim-allographer/example/prepare.nim @@ -0,0 +1,33 @@ +import std/asyncdispatch +import std/json +import std/random +import std/times +import ../src/allographer/query_builder +import ../src/allographer/schema_builder +import ../src/allographer/query_builder/libs/sqlite/sqlite_impl +from connections import rdb + +randomize() + +rdb.create( + table("num_table", [ + Column.integer("id"), + Column.integer("randomnumber") + ]) +) + +seeder rdb, "num_table": + var data = newSeq[JsonNode]() + for i in 1..10000: + data.add(%*{"id": i, "randomnumber": rand(10000)}) + rdb.table("num_table").insert(data).waitFor + +let start = cpuTime() +(proc(){.async.}= + let conn = rdb.pools.conns[0].conn + let stmt = await conn.prepare("select * from num_table where id = ?", 30) + let resultSet = await conn.preparedQuery(@["1"], stmt) + echo resultSet[0] +)() +.waitFor +echo cpuTime() - start diff --git a/clients/nim-allographer/example/rearchtect.nim b/clients/nim-allographer/example/rearchtect.nim new file mode 100644 index 0000000..406b7a0 --- /dev/null +++ b/clients/nim-allographer/example/rearchtect.nim @@ -0,0 +1,48 @@ +import std/asyncdispatch +import std/json +import std/httpclient +import std/streams +import std/options +import ../src/allographer/connection +import ../src/allographer/query_builder + + +proc main() {.async.} = + let client = newAsyncHttpClient() + let response = client.getContent("https://nim-lang.org/assets/img/twitter_banner.png").await + let imageStream = newStringStream(response) + let binaryImage = imageStream.readAll() + + + let rdb = dbOpen(SQLite3, "/root/project/db.sqlite3", shouldDisplayLog=true) + echo rdb.type + + rdb.raw("DROP TABLE IF EXISTS \"test\"").exec().await + + rdb.raw(""" + CREATE TABLE IF NOT EXISTS "test" ( + 'id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + 'blob' BLOB, + 'int' INTEGER, + 'float' NUMERIC, + 'str' VARCHAR + )""" + ).exec().await + + rdb.table("test").insert(%*{"blob":binaryImage, "int": 1, "float": 1.1, "str": "alice"}).await + + let res = rdb.select("id", "int", "float", "str").table("test").get().await + for row in res: + echo row + + var row = rdb.select("id", "int", "float", "str").table("test").first().await + if row.isSome: + echo row.get + + rdb.table("test").where("id", "=", 1).update(%*{"str": "bob"}).await + + row = rdb.select("id", "int", "float", "str").table("test").find(1).await + if row.isSome: + echo row.get + +main().waitFor diff --git a/clients/nim-allographer/example/run.nim b/clients/nim-allographer/example/run.nim new file mode 100644 index 0000000..b094801 --- /dev/null +++ b/clients/nim-allographer/example/run.nim @@ -0,0 +1,131 @@ +import strformat, json, asyncdispatch + +import ../src/allographer/query_builder +import ../src/allographer/schema_builder + +from connections import rdb + +rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("users",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("password").nullable(), + Column.string("address").nullable(), + Column.date("birth_date").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) +]) + +# シーダー +seeder rdb, "auth": + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor() + +seeder rdb, "users": + let total = 20 + var insertData: seq[JsonNode] + for i in 1..total: + let authId = if i mod 2 == 0: 1 else: 2 + insertData.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@gmail.com", + "password": &"password{i}", + "auth_id": authId + } + ) + + rdb.table("users").insert(insertData).waitFor() + + echo rdb.table("users").get().waitFor() + +echo rdb + .select("users.id", "users.email") + .table("users") + .where("name", "=", "user3") + .orWhere("name", "=", "user4") + .orWhere("users.id", "=", 5) + .join("auth", "auth.id", "=", "users.auth_id") + .limit(10) + .get() + .waitFor() + +echo rdb.select("id", "email").table("users").limit(5).get().waitFor() +echo rdb.select("id", "email").table("users").limit(5).first().waitFor() +echo rdb.table("users").find(4).waitFor() +echo rdb.select("id", "email").table("users").limit(5).find(3).waitFor() + +echo "" + +rdb.table("users").insert(%*{"name": "John", "email": "John@gmail.com"}).waitFor() +echo rdb.table("users").insertId(%*{"name": "John", "email": "John@gmail.com"}).waitFor() + + +rdb.table("users").insert( + @[ + %*{"name": "John", "email": "John@gmail.com"}, + %*{"name": "Paul", "email": "Paul@gmail.com"} + ] +) +.waitFor() + +rdb.table("users").insert( + @[ + %*{"name": "Mick", "email": "Mick@gmail.com"}, + %*{"name": "Keith", "password": "KeithPass"} + ] +) +.waitFor() + + +rdb.table("users").where("id", "=", 2).update(%*{"name": "David"}).waitFor() +rdb.table("users").where("id", "=", 2).update(%*{"name": "David"}).waitFor() +echo rdb.select().table("users").where("name", "=", "David").get().waitFor() +echo rdb.table("users").find(2).waitFor() + +rdb.table("users").where("name", "=", "David").delete().waitFor() +rdb.table("users").delete(3).waitFor() +echo rdb.table("users").find(3).waitFor() +echo rdb.table("users").limit(5).get().waitFor() +echo rdb.select("name").table("users").where("address", "is", nil).get().waitFor() + +# sql check +let r = rdb + .select("users.id", "users.email") + .table("users") + .where("name", "=", "user3") + .where("name", "=", "user4") + .where("users.id", "=", 5) + .orWhere("name", "=", "user6") + .orWhere("users.id", "=", 7) + .join("auth", "auth.id", "=", "users.auth_id") + .limit(10) + .get() + .waitFor() +echo r[0]["id"] +echo r[0]["id"].type +echo r[0]["email"] +echo r[0]["email"].type + +type User = ref object + id:int + name:string + email:string + password:string + address:string + birth_date:string + auth_id:int +let users = rdb.table("users").limit(5).get().orm(User).waitFor() +for user in users: + echo user.name + +waitFor rdb.raw("DROP TABLE users").exec() +waitFor rdb.raw("DROP TABLE auth").exec() diff --git a/clients/nim-allographer/example/sarrogate_key.nim b/clients/nim-allographer/example/sarrogate_key.nim new file mode 100644 index 0000000..cf88929 --- /dev/null +++ b/clients/nim-allographer/example/sarrogate_key.nim @@ -0,0 +1,55 @@ +import std/asyncdispatch +import std/strformat +import std/json +import ../src/allographer/query_builder +import ../src/allographer/schema_builder +from ./connections import rdb + + +rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("name") + ]), + table("users",[ + Column.increments("user_id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("password").nullable(), + Column.string("address").nullable(), + Column.date("birth_date").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) +]) + +# シーダー +seeder rdb, "auth": + rdb.table("auth").insert(@[ + %*{"name": "admin"}, + %*{"name": "user"} + ]) + .waitFor + +let total = 20 +var insertData: seq[JsonNode] +for i in 1..total: + let authId = if i mod 2 == 0: 1 else: 2 + insertData.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@gmail.com", + "password": &"password{i}", + "auth_id": authId + } + ) + +rdb.table("users").insert(insertData).waitFor() +echo rdb.table("users").get().waitFor() + +echo rdb.table("users").find(2, key="user_id").waitFor() +echo rdb.table("auth").find(1).waitFor() + +rdb.table("users").delete(2, key="user_id").waitFor() +rdb.table("auth").delete(2).waitFor() +echo rdb.table("users").limit(3).get().waitFor() +echo rdb.table("auth").limit(3).get().waitFor() diff --git a/clients/nim-allographer/example/setup.nim b/clients/nim-allographer/example/setup.nim new file mode 100644 index 0000000..39be9b2 --- /dev/null +++ b/clients/nim-allographer/example/setup.nim @@ -0,0 +1,51 @@ +import + std/strformat, + std/json, + std/asyncdispatch, + ../src/allographer/query_builder, + ../src/allographer/schema_builder + +from ./connections import rdb + +template bench*(msg, body) = + block: + echo "=== " & msg + let start = cpuTime() + body + echo cpuTime() - start + +proc setup*() = + rdb.create( + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) + ) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor + + seeder(rdb, "user"): + var user: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + user.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@mail.com", + "auth_id": authId + } + ) + + rdb.table("user").insert(user).waitFor diff --git a/clients/nim-allographer/example/sqlite/benchmark.nim b/clients/nim-allographer/example/sqlite/benchmark.nim new file mode 100644 index 0000000..1ef7a49 --- /dev/null +++ b/clients/nim-allographer/example/sqlite/benchmark.nim @@ -0,0 +1,126 @@ +# nim c -r --threads:off -d:reset example/sqlite/benchmark.nim + +import std/asyncdispatch +import std/json +import std/os +import std/random +import std/strutils +import std/monotimes +import std/times +import ../../src/allographer/connection +import ../../src/allographer/query_builder +import ../../src/allographer/schema_builder + + +randomize() + +const + range1_10000 = 1..10000 + countNum = 500 + shouldDisplayLog = false + selectSql = "SELECT index as id, randomNumber FROM World WHERE index = ?" + updateSql = "UPDATE World SET randomNumber = ? WHERE index = ?" + + +let + maxConnections = getEnv("DB_MAX_CONNECTION", "95").parseInt + timeout = getEnv("DB_TIMEOUT", "30").parseInt + + sqlitePath = getEnv("SQLITE_PATH", "db.sqlite3") + + +proc timeProcess(name: string, cb: proc(): Future[void]) {.async.} = + var eachTime = 0.0 + var sumTime = 0.0 + const repeatCount = 5 + var resultStr = "" + + for i in 1..repeatCount: + sleep(100) + let start = getMonoTime() + await cb() + eachTime = float64((getMonoTime() - start).inMilliseconds) / 1000.0 + sumTime += eachTime + if i > 1: resultStr.add("\n") + resultStr.add("|" & $i & "|" & $eachTime & "|") + + echo name + echo "|num|time|" + echo "|---|---|" + echo resultStr + echo "|Avg|" & $(sumTime / repeatCount) & "|" + echo "" + + +proc sqliteBenchmarkScenario(rdb: SqliteConnections) {.async.} = + rdb.create( + table("World", [ + Column.increments("index"), + Column.integer("randomNumber").default(0) + ]) + ) + seeder(rdb, "World"): + var data = newSeq[JsonNode]() + for i in range1_10000: + data.add( + %*{"randomNumber": rand(range1_10000)} + ) + await rdb.table("World").insert(data) + + proc benchUpdate() {.async.} = + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + discard rdb.select("index as id", "randomNumber").table("World").where("index", "=", index).first().await + rdb.table("World").where("index", "=", index).update(%*{"randomNumber": number}).await + )() + await all(futures) + + proc benchUpdatePreparedCold() {.async.} = + let selectStmt = rdb.prepare(selectSql) + let updateStmt = rdb.prepare(updateSql) + defer: + selectStmt.close().await + updateStmt.close().await + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + rdb.withConn( + proc(ctx: SqlitePreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @[$index]) + await updateStmt.exec(ctx, @[$number, $index]) + ) + )() + await all(futures) + + let selectStmtWarm = rdb.prepare(selectSql) + let updateStmtWarm = rdb.prepare(updateSql) + + proc benchUpdatePreparedWarm() {.async.} = + var futures = newSeq[Future[void]](countNum) + for i in 1..countNum: + let index = rand(range1_10000) + let number = rand(range1_10000) + futures[i - 1] = (proc(): Future[void] {.async.} = + rdb.withConn( + proc(ctx: SqlitePreparedContext): Future[void] {.async.} = + discard await selectStmtWarm.first(ctx, @[$index]) + await updateStmtWarm.exec(ctx, @[$number, $index]) + ) + )() + await all(futures) + + await timeProcess("sqlite benchmark", benchUpdate) + await timeProcess("sqlite benchmark prepared cold", benchUpdatePreparedCold) + await timeProcess("sqlite benchmark prepared warm", benchUpdatePreparedWarm) + + +proc main() = + let rdb = dbOpen(SQLite3, sqlitePath, maxConnections, timeout, shouldDisplayLog=shouldDisplayLog) + sqliteBenchmarkScenario(rdb).waitFor + +main() diff --git a/clients/nim-allographer/example/surreal.nim b/clients/nim-allographer/example/surreal.nim new file mode 100644 index 0000000..4e72d92 --- /dev/null +++ b/clients/nim-allographer/example/surreal.nim @@ -0,0 +1,209 @@ +import std/asyncdispatch +import std/times +import std/json +import std/options +import ../src/allographer/connection +import ../src/allographer/query_builder + +proc main() {.async.} = + let surreal = await dbOpen(SurrealDb, "test", "test", "user", "pass", "http://surreal", 8000, 10, 30, true, false) + surreal.raw("DELETE type").exec().await + + surreal.table("type").insert(%*[ + { + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") + }, + { + "bool":false, + "string": "bob", + "int": 2, + "float": 1.11, + "datetime": (now() + initDuration(days=1)).format("yyyy-MM-dd'T'HH:mm:sszzz") + } + ]) + .await + + block : + echo "=== get" + echo surreal.table("type").get().await + + block: + echo "=== first" + let res = surreal.table("type").first().await + if res.isSome(): + echo res.get() + + block: + echo "=== find" + let id = SurrealId.new("type:64t0w6gpnye7tdf083ch") + let res = surreal.table("type").find(id).await + if res.isSome(): + echo res.get() + + block: + echo "=== where" + surreal.raw("DELETE auth").exec().await + surreal.raw("DELETE user").exec().await + + let adminId = surreal.table("auth").insertId(%*{"name": "admin"}).await + surreal.table("auth").insert(%*{"name": "editor"}).await + echo surreal.table("auth").get().await + surreal.table("user").insert(%*{"name":"alice", "email":"alice@example.com", "auth": adminId}).await + + let res = surreal.table("user").where("name", "=", "alice").first().await + if res.isSome(): + echo res.get().pretty() + + block: + echo "=== fetch" + let alice = surreal.table("user").where("auth.name", "=", "admin").fetch("auth").first().await + if alice.isSome(): + echo alice.get().pretty() + + block: + echo "=== update" + surreal.raw("DELETE type").exec().await + + let id = surreal.table("type").insertId(%*{ + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") + }) + .await + echo id.rawId() + + surreal.table("type").where("id", "=", id).update(%*{"string": "bob"}).await + echo surreal.table("type").find(id).await + + block: + echo "=== delete all" + surreal.raw("DELETE type").exec().await + + let id = surreal.table("type").insertId(%*{ + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") + }) + .await + + surreal.table("type").delete().await + echo surreal.table("type").get().await + + block: + echo "=== delete row" + surreal.raw("DELETE type").exec().await + + surreal.table("type").insert(%*[ + { + "bool":true, + "string": "alice", + "int": 1, + "float": 3.14, + "datetime": now().format("yyyy-MM-dd'T'HH:mm:sszzz") + }, + { + "bool":false, + "string": "bob", + "int": 2, + "float": 1.11, + "datetime": (now() + initDuration(days=1)).format("yyyy-MM-dd'T'HH:mm:sszzz") + }]) + .await + + let row = surreal.table("type").where("string", "=", "alice").first().await.get() + let rowId = SurrealId.new(row["id"].getStr()) + + surreal.table("type").where("id", "=", rowId).delete().await + echo surreal.table("type").get().await + + block: + echo "=== raw" + surreal.table("type").delete().await + + let define = """ +REMOVE TABLE type; +DEFINE TABLE type SCHEMAFULL; +DEFINE FIELD index ON TABLE type TYPE int; +DEFINE INDEX types_index ON TABLE type COLUMNS index UNIQUE; +DEFINE FIELD bool ON TABLE type TYPE bool; +DEFINE FIELD datetime ON TABLE type TYPE datetime; +DEFINE FIELD float ON TABLE type TYPE float; +DEFINE FIELD int ON TABLE type TYPE int; +DEFINE FIELD string ON TABLE type TYPE string; +""" + surreal.raw(define).exec().await + + echo surreal.raw("INFO FOR TABLE type").info().await + + var rows = surreal.raw("SELECT * FROM type").get().await + let max = rows.len + 1 + + surreal.raw(""" + CREATE type CONTENT { + index: ?, + bool: true, + datetime: ?, + float: 1.11, + int: 1, + string: "aaa" + } + """, + %*[$max, now().format("yyyy-MM-dd'T'HH:mm:sszzz")]) + .exec() + .await + + rows = surreal.raw("SELECT * FROM type").get().await + echo rows + + block: + echo "=== count" + surreal.table("user").delete().await + + surreal.table("user").insert(%*[ + {"name": "alice", "email": "alice@example.com"}, + {"name": "bob", "email": "bob@example.com"}, + {"name": "charlie", "email": "charlie@example.com"}, + ]) + .await + + let count = surreal.table("user").count().await + echo count + + block: + echo "=== max" + surreal.table("user").delete().await + + surreal.table("user").insert(%*[ + {"name": "alice", "email": "alice@example.com", "index": 1}, + {"name": "bob", "email": "bob@example.com", "index": 2}, + {"name": "charlie", "email": "charlie@example.com", "index": 3}, + ]) + .await + + let max = surreal.table("user").max("index").await + echo max + + block: + echo "=== min" + surreal.table("user").delete().await + + surreal.table("user").insert(%*[ + {"name": "alice", "email": "alice@example.com", "index": 1}, + {"name": "bob", "email": "bob@example.com", "index": 2}, + {"name": "charlie", "email": "charlie@example.com", "index": 3}, + ]) + .await + + let min = surreal.table("user").min("index").await + echo min + + +main().waitFor diff --git a/clients/nim-allographer/example/surreal/autoincremets.nim b/clients/nim-allographer/example/surreal/autoincremets.nim new file mode 100644 index 0000000..c31fff4 --- /dev/null +++ b/clients/nim-allographer/example/surreal/autoincremets.nim @@ -0,0 +1,62 @@ +import std/asyncdispatch +import std/json +import ../../src/allographer/query_builder +import ../../src/allographer/schema_builder +import ../../src/allographer/connection + + +let rdb = dbOpen(SurrealDB, "ns", "database", "user", "pass", "http://surreal", 8000, shouldDisplayLog=true).waitFor() + +rdb.raw(""" + REMOVE TABLE `_sequences`; + REMOVE EVENT `set_user_index` ON TABLE `user`; + REMOVE TABLE `user`; + REMOVE TABLE `auth`; +""").exec().waitFor() + +rdb.raw(""" + DEFINE TABLE `_sequences`; + DEFINE FIELD `table_name` ON TABLE `_sequences` TYPE string; + DEFINE FIELD `max_index` ON TABLE `_sequences` TYPE int; + DEFINE INDEX `_sequences_table_name_unique` ON TABLE `_sequences` COLUMNS `table_name` UNIQUE; +""").exec().waitFor() + +rdb.raw(""" + DEFINE TABLE `user`; + INSERT INTO `_sequences` {table_name: "user", max_index: 0}; + DEFINE FIELD `index` ON TABLE `user` TYPE int; + DEFINE FIELD `name` ON TABLE `user` TYPE string; +""").exec().waitFor() + +rdb.raw(""" + DEFINE EVENT `set_user_index` ON TABLE `user` WHEN $event = "CREATE" THEN { + LET $val = (SELECT `max_index` FROM `_sequences` WHERE `table_name` = "user" LIMIT 1)[0].max_index + 1; + UPDATE `user` MERGE {index: $val} WHERE id = $after.id; + UPDATE `_sequences` MERGE {max_index: $val} WHERE table_name = "user"; + }; +""").exec().waitFor() + +echo rdb.raw(""" + INSERT INTO `user` {name: "user1"}; + SELECT * FROM `user`; + SELECT * FROM `_sequences`; +""").info().waitFor() + +echo rdb.raw(""" + UPDATE `user` MERGE {name: "updated"} WHERE `name` = "user1"; + SELECT * FROM `user`; + SELECT * FROM `_sequences`; +""").info().waitFor() + + +echo rdb.raw(""" + INSERT INTO `user` {name: "user2"}; + SELECT * FROM `user`; + SELECT * FROM `_sequences`; +""").info().waitFor() + +echo rdb.raw(""" + UPDATE `user` MERGE {name: "updated2"} WHERE `name` = "user2"; + SELECT * FROM `user`; + SELECT * FROM `_sequences`; +""").info().waitFor() diff --git a/clients/nim-allographer/example/surreal/test1.nim b/clients/nim-allographer/example/surreal/test1.nim new file mode 100644 index 0000000..c3edd35 --- /dev/null +++ b/clients/nim-allographer/example/surreal/test1.nim @@ -0,0 +1,49 @@ +import std/asyncdispatch +import std/httpclient +import std/streams +import std/json +import ../../src/allographer/connection +import ../../src/allographer/query_builder +import ../../src/allographer/schema_builder + +proc main() {.async.} = + let rdb = dbOpen(SurrealDB, "ns", "database", "user", "pass", "http://surreal", 8000, shouldDisplayLog=true).await + + rdb.raw(""" + REMOVE TABLE `test` + """).exec().await + + rdb.raw(""" + DEFINE TABLE `test` SCHEMAFULL; + DEFINE FIELD `bool` ON TABLE `test` TYPE bool; + DEFINE FIELD `int` ON TABLE `test` TYPE int; + DEFINE FIELD `float` ON TABLE `test` TYPE decimal; + DEFINE FIELD `str` ON TABLE `test` TYPE string; + DEFINE FIELD `data` ON TABLE `test` TYPE string | NONE; + DEFINE FIELD `object` ON TABLE `test` TYPE object | NONE FLEXIBLE; + """).exec().waitFor + + echo rdb.table("test").columns().waitFor + + rdb.raw(""" + INSERT INTO `test` {bool:?, int:?, float:?, str:?, object:?} + """, %*[true, 1, 1.1, "alice", {"bool": false, "int": 2, "float": 2.1, "str": "two"}]).exec().waitFor + + echo rdb.raw(""" + SELECT * FROM `test` + """).get().waitFor + + let client = newAsyncHttpClient() + var veryLongText = "" + var response = client.getContent("https://bible-api.com/genesis+1:1-31").await.parseJson + veryLongText.add(response["text"].getStr) + response = client.getContent("https://bible-api.com/genesis+2:1-25").await.parseJson + veryLongText.add(response["text"].getStr) + response = client.getContent("https://bible-api.com/genesis+3:1-24").await.parseJson + veryLongText.add(response["text"].getStr) + + rdb.table("test").insert(%*{"bool":false, "int":2, "float":2.1, "str": veryLongText}).waitFor + + echo rdb.select("bool", "int", "float", "str").table("test").get().waitFor + +main().waitFor diff --git a/clients/nim-allographer/example/surrealDb.nim b/clients/nim-allographer/example/surrealDb.nim new file mode 100644 index 0000000..5900bbd --- /dev/null +++ b/clients/nim-allographer/example/surrealDb.nim @@ -0,0 +1,17 @@ +import std/asyncdispatch +import std/json +import ../src/allographer/connection +import ../src/allographer/query_builder + + +let db = dbOpen(SurrealDB, "test", "test", "user", "pass", "http://surreal", 8000, 10, 30, true, true).waitFor() +db.raw("DELETE FROM account").exec().waitFor +echo db.raw("INFO FOR DB").get().waitFor + +for _ in 0..3: + db.raw("CREATE account SET name = 'ACME Inc', created_at = time::now()").exec().waitFor + +let resp = db.raw("SELECT * from account").get().waitFor +for row in resp: + echo row + echo row["id"].getStr diff --git a/clients/nim-allographer/example/transaction.nim b/clients/nim-allographer/example/transaction.nim new file mode 100644 index 0000000..52a17e5 --- /dev/null +++ b/clients/nim-allographer/example/transaction.nim @@ -0,0 +1,56 @@ +import json, asyncdispatch, strformat +import ../src/allographer/schema_builder +import ../src/allographer/query_builder +import ./connections +import ./setup + + +proc main(){.async.} = + rdb.create( + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("users",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) + ) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]).waitFor + + seeder(rdb, "users"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@gmail.com", + "auth_id": authId + } + ) + rdb.table("users").insert(users).waitFor + + echo "=====" + transaction(rdb): + assert rdb.isInTransaction == true + echo rdb.select("name", "email").table("users").where("id", "=", 2).get().await + + echo "=====" + transaction(rdb): + assert rdb.isInTransaction == true + rdb.table("table").insert(%*{"aaa": "bbb"}).await + echo rdb.table("aaa").get().await + + assert rdb.isInTransaction == false + +main().waitFor diff --git a/clients/nim-allographer/example/typedReturn.nim b/clients/nim-allographer/example/typedReturn.nim new file mode 100644 index 0000000..608a81e --- /dev/null +++ b/clients/nim-allographer/example/typedReturn.nim @@ -0,0 +1,35 @@ +import times, json, macros + +# type Type = ref object +# id: int +# name: string +# birth_date: DateTime + +# let response = Type(id:1, name:"John", birth_date: "1990-01-01".parse("yyyy-MM-dd")) +var typ: tuple[id:int, name:string, birth_date:DateTime] +var response = @[ + %*{"id": 1, "name":"user1", "birth_date":"1990-01-01"}, + %*{"id": 2, "name":"user2", "birth_date":"1990-01-02"} + ] + +macro orm(head, body: untyped) = + echo repr(head) + echo repr(body) + +orm typ: + response +# for v in typ.fields: +# echo v + +#[ + +proc orm(response_arg:openArray[JsonNode], typ:var tuple):seq[typ.type] = + var response: seq[typ.type] + for row in response_arg: + typ.id = row["id"].getInt() + typ.name = row["name"].getStr() + typ.birth_date = row["birth_date"].getStr().parse("yyyy-MM-dd") + response.add(typ) + return response + +]# diff --git a/clients/nim-allographer/example/upperCase.nim b/clients/nim-allographer/example/upperCase.nim new file mode 100644 index 0000000..9544f1a --- /dev/null +++ b/clients/nim-allographer/example/upperCase.nim @@ -0,0 +1,10 @@ +import json +import ../src/allographer/connection +import ../src/allographer/query_builder + +let rdb = dbOpen(SQLite3, ":memory:", shouldDisplayLog=false) + +discard rdb.table("world").where("id", "=", 1).update(%*{"randomnumber": 2}) +discard rdb.table("World").where("Id", "=", 1).update(%*{"randomNumber": 2}) +discard rdb.select("id", "randomnumber").table("world").where("id", "=", 1).get() +discard rdb.select("id", "randomNumber").table("World").where("Id", "=", 1).get() diff --git a/clients/nim-allographer/makefile b/clients/nim-allographer/makefile new file mode 100644 index 0000000..fd3cb0f --- /dev/null +++ b/clients/nim-allographer/makefile @@ -0,0 +1,9 @@ +exec: + docker compose start + docker compose exec app bash + +stop: + docker compose stop + +diff: + git diff --cached > .diff diff --git a/clients/nim-allographer/permission.sh b/clients/nim-allographer/permission.sh new file mode 100755 index 0000000..99bc322 --- /dev/null +++ b/clients/nim-allographer/permission.sh @@ -0,0 +1,8 @@ +local_UID=$(id -u $USER) +local_GID=$(id -g $USER) + +echo 'start' +sudo chown ${local_UID}:${local_GID} * -R +echo '======================' +sudo chown ${local_UID}:${local_GID} .* -R +echo 'end' diff --git a/clients/nim-allographer/src/allographer/connection.nim b/clients/nim-allographer/src/allographer/connection.nim new file mode 100644 index 0000000..1decced --- /dev/null +++ b/clients/nim-allographer/src/allographer/connection.nim @@ -0,0 +1,28 @@ +import ./env + +import ./query_builder/libs/database_url +export DatabaseUrl, DatabaseUrlQuery, ParsedDatabaseUrl, asDatabaseUrl, parseDatabaseUrl, databaseName, sqliteDatabasePath, portOrDefault, requireDatabaseUrlScheme + +when isExistsSqlite: + import ./query_builder/models/sqlite/sqlite_types; export SQLite3, SqliteConnections, SqlitePreparedContext + import ./query_builder/models/sqlite/sqlite_open; export sqlite_open + +when isExistsPostgres: + import ./query_builder/models/postgres/postgres_types; export PostgreSQL, PostgresConnections, PostgresPreparedContext + import ./query_builder/models/postgres/postgres_open; export postgres_open + +when isExistsMariadb: + import ./query_builder/models/mariadb/mariadb_types; export MariaDB, MariadbConnections, MariadbPreparedContext + import ./query_builder/models/mariadb/mariadb_open; export mariadb_open + +when isExistsMysql: + import ./query_builder/models/mysql/mysql_types; export MySql, MysqlConnections, MysqlPreparedContext + import ./query_builder/models/mysql/mysql_open; export mysql_open + +when isExistsSurrealdb: + import ./query_builder/models/surreal/surreal_types; export SurrealDB, SurrealConnections, SurrealPreparedContext + import ./query_builder/models/surreal/surreal_open; export surreal_open + +when isExistsBaradb: + import ./query_builder/models/baradb/baradb_types; export Baradb, BaradbConnections, BaradbPreparedContext + import ./query_builder/models/baradb/baradb_open; export baradb_open diff --git a/clients/nim-allographer/src/allographer/env.nim b/clients/nim-allographer/src/allographer/env.nim new file mode 100644 index 0000000..68af529 --- /dev/null +++ b/clients/nim-allographer/src/allographer/env.nim @@ -0,0 +1,27 @@ +import std/os +import std/strutils +import std/streams +import std/parsecfg + +for f in walkDir(getCurrentDir()): + if f.path.split("/")[^1] == ".env": + let path = getCurrentDir() / ".env" + var f = newFileStream(path, fmRead) + var p: CfgParser + open(p, f, path) + while true: + let e = next(p) + case e.kind + of cfgEof: break + of cfgKeyValuePair: putEnv(e.key, e.value) + else: discard + break + + +const + isExistsSqlite* = getEnv("DB_SQLITE", $false).parseBool + isExistsPostgres* = getEnv("DB_POSTGRES", $false).parseBool + isExistsMysql* = getEnv("DB_MYSQL", $false).parseBool + isExistsMariadb* = getEnv("DB_MARIADB", $false).parseBool + isExistsSurrealdb* = getEnv("DB_SURREAL", $false).parseBool + isExistsBaradb* = getEnv("DB_BARADB", $false).parseBool diff --git a/clients/nim-allographer/src/allographer/query_builder.nim b/clients/nim-allographer/src/allographer/query_builder.nim new file mode 100644 index 0000000..776c507 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder.nim @@ -0,0 +1,41 @@ +import ./env + + +import ./query_builder/enums; export enums +import ./query_builder/error; export error +import ./query_builder/models/orm; export orm + +when isExistsSqlite: + import ./query_builder/models/sqlite/sqlite_types; export SQLite3, SqliteConnections, SqlitePreparedContext, sqlite_types + import ./query_builder/models/sqlite/sqlite_query; export sqlite_query + import ./query_builder/models/sqlite/sqlite_exec; export sqlite_exec + import ./query_builder/models/sqlite/sqlite_transaction; export sqlite_transaction + +when isExistsPostgres: + import ./query_builder/models/postgres/postgres_types; export postgres_types + import ./query_builder/models/postgres/postgres_query; export postgres_query + import ./query_builder/models/postgres/postgres_exec; export postgres_exec + import ./query_builder/models/postgres/poatgres_transaction; export poatgres_transaction + +when isExistsMariadb: + import ./query_builder/models/mariadb/mariadb_types; export mariadb_types + import ./query_builder/models/mariadb/mariadb_query; export mariadb_query + import ./query_builder/models/mariadb/mariadb_exec; export mariadb_exec + import ./query_builder/models/mariadb/mariadb_transaction; export mariadb_transaction + +when isExistsMysql: + import ./query_builder/models/mysql/mysql_types; export mysql_types + import ./query_builder/models/mysql/mysql_query; export mysql_query + import ./query_builder/models/mysql/mysql_exec; export mysql_exec + import ./query_builder/models/mysql/mysql_transaction; export mysql_transaction + +when isExistsSurrealdb: + import ./query_builder/models/surreal/surreal_types; export surreal_types + import ./query_builder/models/surreal/surreal_query; export surreal_query + import ./query_builder/models/surreal/surreal_exec; export surreal_exec + +when isExistsBaradb: + import ./query_builder/models/baradb/baradb_types; export baradb_types + import ./query_builder/models/baradb/baradb_query; export baradb_query + import ./query_builder/models/baradb/baradb_exec; export baradb_exec + import ./query_builder/models/baradb/baradb_transaction; export baradb_transaction diff --git a/clients/nim-allographer/src/allographer/query_builder.out b/clients/nim-allographer/src/allographer/query_builder.out new file mode 100755 index 0000000000000000000000000000000000000000..7006367b1cbdd9c364c3439e0fb6433150a9b3f1 GIT binary patch literal 328792 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%0AQ}y{Ir&SU1ObICyoIo(T97B)z-a7-Ni? z;>*RIoo$mNPS@aKbo0i=(qwsj*Eou=3(6=o-VlW7gLof_^my;4h0eupyt%zPyty;B zePMps#<)x-r}4T9PRZ)ej8jZXkWL??8Y>nHBW1))rMKL7Loklijx|onpzgIs>V8n) zF&S|m?+CZ!t!AowOG`CMSm^1#W^NQF2OWsXWD804UNCctzFC9?hmHcrWDM&Q_jJ@` z*kbo0kOY<+4r`%tZV$`_KZ;WcPCNDKOXDhrgrS4k#Q5TBBUITV@6cV|snIyrXzbLn z(9~pSVWJ10E@^Kci*=R;ssmdl;Ac%~m%k=x)g{Y0wY95Xa#+6fzbps-D(i`iA91 zqVQ5v4EFbOZnKUV-UzH46H+2Qy?(^iT3cO znVpj}>lc=GO}E#L-nOakTzq3Fo7olWa(1p8=vnMt!to=My_8687|2dcZ(o|+mY?Zt z8(G>llbutV=)fR1j+m(io2YcNik*!uc6NG3DMN#qhc`f7pN`K=I{D1fu7UBcTu*;~ zb|T)t9tEh`*`BM-&ieRKLv!8UK#c4??f_1D7IAV`#~;Svhx)n=?N%lS@1(}R!Jr$n z8;xLdeah)mt8SL(nyfMA8cc?6uG=;CasO}=LTvzIu5LaUm0O90i#k1|S=Nhe#j;0E zf#c4nm>m;8Y&aZ_0Lx;wK38iyuJ^oZ%~cS-4K~!xgZRsxvZfwc1m+w|EVCuuu)vze zF;BGK&Wu&^IH;m`ACgIZ=^)X@TL;^r?YMNX9Zre9b5Q$7qD|d7fLHC2X%qVnw_C3? znk-hrDvT2_Jz)7jJsh3N4rZoj*KO`<+Zvk~#)}s4`4gu{&Hq@pFI&t_IfGbkR7-j} zSs$FSFkB&=PgXWRiSxa9{ifQdYLIlbc0$1T++OD%)^Ez|UnP_2#gtko=)+G93|;2Q znCuv$wz_0%dK_^KGx};F4J`D7L<6TekOa#NeSNfZ$`z3D4nlRh&p1c5vDA(=vTOa@HMU!cWwB@E=d zev;4pTjfU*B*lalgPPRkmR&v4eB z_<~}?kDs5cVW;3`A1ciF%Lp8U5l zzj?M8LO#9uw*de1OD`2o{U2q1^EBU2KA8Ev^-lpm9RHd8hq3_k>^Mj=sx~Uwnf&Iv z!}#2;rHVB9`(KVf@H5X-O@hGY^Pl|7vgYuwmt6HPsmkD2d&-}dslQ=op64U|@2>p& znBP2iFu%9{gsu1EAF9cQUk5%7s($AA?N`g+rhISvuR^lQ-z8+$p>L3XnCEWFQ{{gR z{`2PlG%zJs!@u@jQg+;NNOS+lt_FPq|8nPVVLtO*O?@yK%=cZ*^LkHy#NqYNJWYK~ ze)G3^-t5V5z8h_xUp%0(0;XKU?{-iA{rt|ec~YGWxt4Y95B%d!<~RLkzHe@x&-J;f zEvu#RPwQKr{Lft?^)=5Ik8aH1mH)%u{2yn2KKsWT^BcJt^ym18!d8)SD_dxuTiG9m z-kbjkB)n4PCxOXlo?d$Mw|PE|d`G(Sx3hfnG<0NN(_e!>j|8QEru+`(H_yFCn4I!# zY5dbV1eulYoBaEZ5JA+QiaCC2{HmWR*XYqrz})5Eb)@7s&(n0S`u~{x=D8Y~-T8mb z{N{Nz%QqZMKJ$FFC;x7KSKd6gdK9L`Fyiwh" + of fkArray: return "" + of fkObject: return "" + of fkVector: return "" + of fkJson: return wv.jsonVal + +proc readQueryResponse(client: BaraClient): Future[QueryResult] {.async.} = + let headerData = await client.socket.recv(12) + if headerData.len < 12: + raise newException(IOError, "Connection closed") + + var pos = 0 + let hdrData = toBytes(headerData) + let kind = MsgKind(readUint32(hdrData, pos)) + let payloadLen = int(readUint32(hdrData, pos)) + discard readUint32(hdrData, pos) + + let payloadStr = await client.socket.recv(payloadLen) + var payload = toBytes(payloadStr) + + result = QueryResult(columns: @[], rows: @[], rowCount: 0, affectedRows: 0) + + if kind == mkReady: + return + if kind == mkError and payload.len >= 8: + var epos = 0 + let code = readUint32(payload, epos) + let emsg = readString(payload, epos) + raise newException(IOError, "Error " & $code & ": " & emsg) + if kind == mkData: + var dpos = 0 + let colCount = int(readUint32(payload, dpos)) + var cols: seq[string] = @[] + for i in 0..= 12: + var chPos = 0 + let chData = toBytes(compHeader) + let compKind = MsgKind(readUint32(chData, chPos)) + let compLen = int(readUint32(chData, chPos)) + discard readUint32(chData, chPos) + let compPayloadStr = await client.socket.recv(compLen) + if compKind == mkComplete: + var cpPos = 0 + result.affectedRows = int(readUint32(toBytes(compPayloadStr), cpPos)) + return + if kind == mkComplete: + var rpos = 0 + result.affectedRows = int(readUint32(payload, rpos)) + return + +proc query*(client: BaraClient, sql: string): Future[QueryResult] {.async.} = + if not client.connected: + raise newException(IOError, "Not connected") + + let msg = makeQueryMessage(client.nextId(), sql) + let msgStr = toString(msg) + await client.socket.send(msgStr) + + return await client.readQueryResponse() + +proc query*(client: BaraClient, sql: string, params: seq[WireValue]): Future[QueryResult] {.async.} = + if not client.connected: + raise newException(IOError, "Not connected") + + let msg = makeQueryParamsMessage(client.nextId(), sql, params) + let msgStr = toString(msg) + await client.socket.send(msgStr) + + return await client.readQueryResponse() + +proc exec*(client: BaraClient, sql: string): Future[int] {.async.} = + let qr = await client.query(sql) + return qr.affectedRows + +proc auth*(client: BaraClient, token: string) {.async.} = + if not client.connected: + raise newException(IOError, "Not connected") + + let msg = makeAuthMessage(client.nextId(), token) + let msgStr = toString(msg) + await client.socket.send(msgStr) + + let headerData = await client.socket.recv(12) + if headerData.len < 12: + raise newException(IOError, "Connection closed") + + var pos = 0 + let hdrData = toBytes(headerData) + let kind = MsgKind(readUint32(hdrData, pos)) + let payloadLen = int(readUint32(hdrData, pos)) + discard readUint32(hdrData, pos) + + if kind == mkAuthOk: + return + elif kind == mkError: + let payloadStr = await client.socket.recv(payloadLen) + var epos = 0 + let emsg = readString(toBytes(payloadStr), epos) + raise newException(IOError, "Auth failed: " & emsg) + else: + raise newException(IOError, "Unexpected auth response: 0x" & toHex(uint32(kind), 2)) + +proc ping*(client: BaraClient): Future[bool] {.async.} = + if not client.connected: + return false + let msg = buildMessage(mkPing, client.nextId(), @[]) + let msgStr = toString(msg) + await client.socket.send(msgStr) + + let headerData = await client.socket.recv(12) + if headerData.len < 12: + return false + + var pos = 0 + let hdrData = toBytes(headerData) + let kind = MsgKind(readUint32(hdrData, pos)) + return kind == mkPong + +# === Fluent Query Builder === + +type + QueryBuilder* = ref object + client: BaraClient + selectCols: seq[string] + fromTable: string + whereClauses: seq[string] + joinClauses: seq[string] + groupByCols: seq[string] + havingClause: string + orderCols: seq[string] + orderDirs: seq[string] + limitVal: int + offsetVal: int + +proc newQueryBuilder*(client: BaraClient): QueryBuilder = + QueryBuilder(client: client, limitVal: 0, offsetVal: 0) + +proc select*(qb: QueryBuilder, cols: varargs[string]): QueryBuilder = + for c in cols: qb.selectCols.add(c) + return qb + +proc `from`*(qb: QueryBuilder, table: string): QueryBuilder = + qb.fromTable = table + return qb + +proc where*(qb: QueryBuilder, clause: string): QueryBuilder = + qb.whereClauses.add(clause) + return qb + +proc join*(qb: QueryBuilder, table: string, on: string): QueryBuilder = + qb.joinClauses.add("JOIN " & table & " ON " & on) + return qb + +proc leftJoin*(qb: QueryBuilder, table: string, on: string): QueryBuilder = + qb.joinClauses.add("LEFT JOIN " & table & " ON " & on) + return qb + +proc groupBy*(qb: QueryBuilder, cols: varargs[string]): QueryBuilder = + for c in cols: qb.groupByCols.add(c) + return qb + +proc having*(qb: QueryBuilder, clause: string): QueryBuilder = + qb.havingClause = clause + return qb + +proc orderBy*(qb: QueryBuilder, col: string, dir: string = "ASC"): QueryBuilder = + qb.orderCols.add(col) + qb.orderDirs.add(dir) + return qb + +proc limit*(qb: QueryBuilder, n: int): QueryBuilder = + qb.limitVal = n + return qb + +proc offset*(qb: QueryBuilder, n: int): QueryBuilder = + qb.offsetVal = n + return qb + +proc build*(qb: QueryBuilder): string = + result = "SELECT " & (if qb.selectCols.len > 0: qb.selectCols.join(", ") else: "*") + result &= " FROM " & qb.fromTable + for j in qb.joinClauses: result &= " " & j + if qb.whereClauses.len > 0: result &= " WHERE " & qb.whereClauses.join(" AND ") + if qb.groupByCols.len > 0: result &= " GROUP BY " & qb.groupByCols.join(", ") + if qb.havingClause.len > 0: result &= " HAVING " & qb.havingClause + if qb.orderCols.len > 0: + result &= " ORDER BY " + for i, col in qb.orderCols: + if i > 0: result &= ", " + result &= col & " " & qb.orderDirs[i] + if qb.limitVal > 0: result &= " LIMIT " & $qb.limitVal + if qb.offsetVal > 0: result &= " OFFSET " & $qb.offsetVal + +proc exec*(qb: QueryBuilder): Future[QueryResult] {.async.} = + return await qb.client.query(qb.build()) + +# === Blocking Sync Client (production-grade, no waitFor) === + +type + SyncClient* = ref object + config: ClientConfig + socket: netmod.Socket + connected: bool + requestId: uint32 + lock: Lock + +proc newSyncClient*(config: ClientConfig = defaultConfig()): SyncClient = + result = SyncClient(config: config, connected: false, requestId: 0) + result.socket = netmod.newSocket() + initLock(result.lock) + +proc recvExact(sock: netmod.Socket, size: int): string = + result = "" + while result.len < size: + let chunk = sock.recv(size - result.len) + if chunk.len == 0: + raise newException(IOError, "Connection closed") + result.add(chunk) + +proc readQueryResponseBlocking(client: SyncClient): QueryResult = + let headerData = client.socket.recvExact(12) + var pos = 0 + let hdrData = toBytes(headerData) + let kind = MsgKind(readUint32(hdrData, pos)) + let payloadLen = int(readUint32(hdrData, pos)) + discard readUint32(hdrData, pos) + + let payloadStr = client.socket.recvExact(payloadLen) + var payload = toBytes(payloadStr) + + result = QueryResult(columns: @[], rows: @[], rowCount: 0, affectedRows: 0) + + if kind == mkReady: + return + if kind == mkError and payload.len >= 8: + var epos = 0 + let code = readUint32(payload, epos) + let emsg = readString(payload, epos) + raise newException(IOError, "Error " & $code & ": " & emsg) + if kind == mkData: + var dpos = 0 + let colCount = int(readUint32(payload, dpos)) + var cols: seq[string] = @[] + for i in 0.. 0: + result.port = parsed.port.parseInt() + result.hasPort = true + + +proc parseDatabaseUrl*(value: DatabaseUrl): ParsedDatabaseUrl = + parseDatabaseUrl(string(value)) + + +proc requireDatabaseUrlScheme*(value: ParsedDatabaseUrl; expectedSchemes: openArray[string]; + driverName: string) = + if value.scheme.len == 0 or value.scheme notin expectedSchemes: + raise newException( + ValueError, + "Invalid URL format. Expected a " & driverName & " URL starting with '" & + expectedSchemes[0] & "://'." + ) + + +proc stripLeadingSlash(value: string): string = + if value.len > 0 and value[0] == '/': + if value.len == 1: + return "" + return value[1..^1] + value + + +proc databaseName*(value: ParsedDatabaseUrl): string = + stripLeadingSlash(value.path) + + +proc sqliteDatabasePath*(value: ParsedDatabaseUrl): string = + if value.hostname.len == 0: + return value.path + + let tail = stripLeadingSlash(value.path) + if tail.len == 0: + return value.hostname + + result = value.hostname & "/" & tail + + +proc portOrDefault*(value: ParsedDatabaseUrl; defaultPort: int): int = + if value.hasPort: + value.port + else: + defaultPort diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_impl.nim b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_impl.nim new file mode 100644 index 0000000..c20af55 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_impl.nim @@ -0,0 +1,558 @@ +import std/asyncdispatch +import std/monotimes +import std/times +import std/json +import ../../error +import ../../models/database_types +import ../../models/mariadb/mariadb_types +import ../../prepared_param +import ./mariadb_rdb +import ./mariadb_lib + + +type + MariadbWaitState = object + cancelled: bool + +proc mariadbTimeoutError(): ref DbError = + newException(DbError, "MariaDB query timeout") + +proc makeDeadline(timeout: int): MonoTime = + if timeout > 0: + result = getMonoTime() + initDuration(seconds = timeout) + else: + result = getMonoTime() + +proc remainingMs(deadline: MonoTime): int = + let left = (deadline - getMonoTime()).inMilliseconds + if left <= 0: + return 0 + if left > int64(high(int)): + return high(int) + result = int(left) + if result < 1: + result = 1 + +proc ensureMariadbSocketRegistered(conn: PMySQL): AsyncFD = + let sock = mariadb_rdb.get_socket(conn) + if sock < 0: + dbError(conn) + result = AsyncFD(cint(sock)) + let disp = getGlobalDispatcher() + if not disp.contains(result): + register(result) + +proc waitMariadb(conn: PMySQL, waitStatus: cint, deadline: MonoTime): Future[cint] {.async.} = + if waitStatus == 0: + return 0 + + let totalRemaining = remainingMs(deadline) + if totalRemaining <= 0: + raise mariadbTimeoutError() + + var stepTimeout = totalRemaining + if (waitStatus and MYSQL_WAIT_TIMEOUT) != 0: + let connectorTimeout = int(mariadb_rdb.get_timeout_value_ms(conn)) + if connectorTimeout > 0 and connectorTimeout < stepTimeout: + stepTimeout = connectorTimeout + if stepTimeout < 1: + stepTimeout = 1 + + let shouldWaitRead = (waitStatus and (MYSQL_WAIT_READ or MYSQL_WAIT_EXCEPT)) != 0 + let shouldWaitWrite = (waitStatus and MYSQL_WAIT_WRITE) != 0 + + if not shouldWaitRead and not shouldWaitWrite: + await sleepAsync(stepTimeout) + return cint(MYSQL_WAIT_TIMEOUT) + + let fd = ensureMariadbSocketRegistered(conn) + var state = MariadbWaitState(cancelled: false) + var ioReady = newFuture[void]("waitMariadb") + var readyStatus = 0.cint + + if shouldWaitRead: + proc readCb(f: AsyncFD): bool = + if state.cancelled: + return true + readyStatus = readyStatus or cint(MYSQL_WAIT_READ) + if (waitStatus and MYSQL_WAIT_EXCEPT) != 0: + readyStatus = readyStatus or cint(MYSQL_WAIT_EXCEPT) + if not ioReady.finished: + ioReady.complete() + return true + addRead(fd, readCb) + + if shouldWaitWrite: + proc writeCb(f: AsyncFD): bool = + if state.cancelled: + return true + readyStatus = readyStatus or cint(MYSQL_WAIT_WRITE) + if not ioReady.finished: + ioReady.complete() + return true + addWrite(fd, writeCb) + + let ok = await withTimeout(ioReady, stepTimeout) + if not ok: + state.cancelled = true + unregister(fd) + if (waitStatus and MYSQL_WAIT_TIMEOUT) != 0: + return cint(MYSQL_WAIT_TIMEOUT) + raise mariadbTimeoutError() + + if readyStatus == 0: + if (waitStatus and MYSQL_WAIT_TIMEOUT) != 0: + return cint(MYSQL_WAIT_TIMEOUT) + return waitStatus and cint(MYSQL_WAIT_READ or MYSQL_WAIT_WRITE or MYSQL_WAIT_EXCEPT) + + return readyStatus + +proc runRealQuery(conn: PMySQL, q: string, deadline: MonoTime): Future[void] {.async.} = + var queryStatus = 0'i32 + var waitStatus = mariadb_rdb.real_query_start(addr queryStatus, conn, q.cstring, culong(q.len)) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = mariadb_rdb.real_query_cont(addr queryStatus, conn, ready) + if queryStatus != 0'i32: + dbError(conn) + +proc runFetchRow(conn: PMySQL, sqlres: PRES, deadline: MonoTime): Future[mariadb_rdb.Row] {.async.} = + var row: mariadb_rdb.Row = nil + var waitStatus = mariadb_rdb.fetch_row_start(addr row, sqlres) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = mariadb_rdb.fetch_row_cont(addr row, sqlres, ready) + return row + +proc runFreeResult(conn: PMySQL, sqlres: PRES, deadline: MonoTime): Future[void] {.async.} = + if sqlres.isNil: + return + var waitStatus = mariadb_rdb.free_result_start(sqlres) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = mariadb_rdb.free_result_cont(sqlres, ready) + +proc rawExec(conn: PMySQL, query: string, args: MariadbParams, timeout: int) {.async.} = + let q = dbFormat(conn, query, args) + let deadline = makeDeadline(timeout) + await runRealQuery(conn, q, deadline) + + +proc runStmtPrepare(conn: PMySQL, stmt: PSTMT, sql: string, deadline: MonoTime): Future[void] {.async.} = + var ret = 0.cint + var waitStatus = stmt_prepare_start(addr ret, stmt, sql.cstring, culong(sql.len)) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = stmt_prepare_cont(addr ret, stmt, ready) + if ret != 0: + raise newException(DbError, $stmt_error(stmt)) + + +proc runStmtReset(conn: PMySQL, stmt: PSTMT, deadline: MonoTime): Future[void] {.async.} = + var ret = false + var waitStatus = stmt_reset_start(addr ret, stmt) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = stmt_reset_cont(addr ret, stmt, ready) + if ret: + raise newException(DbError, $stmt_error(stmt)) + + +proc runStmtFreeResult(conn: PMySQL, stmt: PSTMT, deadline: MonoTime): Future[void] {.async.} = + var ret = false + var waitStatus = stmt_free_result_start(addr ret, stmt) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = stmt_free_result_cont(addr ret, stmt, ready) + if ret: + raise newException(DbError, $stmt_error(stmt)) + + +proc runStmtExecute(conn: PMySQL, stmt: PSTMT, deadline: MonoTime): Future[void] {.async.} = + var ret = 0.cint + var waitStatus = stmt_execute_start(addr ret, stmt) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = stmt_execute_cont(addr ret, stmt, ready) + if ret != 0: + raise newException(DbError, $stmt_error(stmt)) + + +proc runStmtStoreResult(conn: PMySQL, stmt: PSTMT, deadline: MonoTime): Future[void] {.async.} = + var ret = 0.cint + var waitStatus = stmt_store_result_start(addr ret, stmt) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = stmt_store_result_cont(addr ret, stmt, ready) + if ret != 0: + raise newException(DbError, $stmt_error(stmt)) + + +proc runStmtFetch(conn: PMySQL, stmt: PSTMT, deadline: MonoTime): Future[cint] {.async.} = + var ret = 0.cint + var waitStatus = stmt_fetch_start(addr ret, stmt) + while waitStatus != 0: + let ready = await waitMariadb(conn, waitStatus, deadline) + waitStatus = stmt_fetch_cont(addr ret, stmt, ready) + return ret + + +proc prepareStmt*(conn: PMySQL, sql: string, timeout: int): Future[PSTMT] {.async.} = + assert(not conn.isNil, "Database not connected.") + result = stmt_init(conn) + if result.isNil: + dbError(conn) + let deadline = makeDeadline(timeout) + try: + await runStmtPrepare(conn, result, sql, deadline) + except CatchableError: + discard stmt_close(result) + raise + + +proc bindStmtParams(stmt: PSTMT, args: seq[PreparedParam]) = + if stmt_param_count(stmt) != args.len: + raise newException(DbError, "Prepared statement parameter count mismatch.") + + if args.len == 0: + return + + var binds = newSeq[BIND](args.len) + var values = newSeq[string](args.len) + var lengths = newSeq[culong](args.len) + var nullFlags = newSeq[my_bool](args.len) + var errorFlags = newSeq[my_bool](args.len) + + for i, arg in args: + if arg.isNull: + nullFlags[i] = true + binds[i].buffer_type = TYPE_NULL + binds[i].is_null = addr nullFlags[i] + binds[i].error = addr errorFlags[i] + continue + + values[i] = arg.value + lengths[i] = values[i].len.culong + binds[i].buffer_type = TYPE_STRING + if values[i].len > 0: + binds[i].buffer = cast[pointer](values[i].cstring) + else: + binds[i].buffer = nil + binds[i].buffer_length = values[i].len.culong + binds[i].length = addr lengths[i] + binds[i].is_null = addr nullFlags[i] + binds[i].error = addr errorFlags[i] + + if stmt_bind_param(stmt, binds[0].addr): + raise newException(DbError, $stmt_error(stmt)) + + +proc bindStmtResults( + stmt: PSTMT, + metadata: PRES, + resultBinds: MariadbResultBindCache +) = + let cols = int(num_fields(metadata)) + if resultBinds.binds.len != cols: + resultBinds.binds = newSeq[BIND](cols) + resultBinds.buffers = newSeq[string](cols) + resultBinds.lengths = newSeq[culong](cols) + resultBinds.nullFlags = newSeq[my_bool](cols) + resultBinds.errorFlags = newSeq[my_bool](cols) + for i in 0 ..< cols: + let field = fetch_field_direct(metadata, cast[mariadb_rdb.cuint](i)) + var bufferLen = int(field.len) + if bufferLen < 4096: + bufferLen = 4096 + resultBinds.buffers[i] = newString(bufferLen) + else: + for i in 0 ..< cols: + resultBinds.lengths[i] = 0 + resultBinds.nullFlags[i] = false + resultBinds.errorFlags[i] = false + + for i in 0 ..< cols: + resultBinds.binds[i].buffer_type = TYPE_STRING + resultBinds.binds[i].buffer = if resultBinds.buffers[i].len > 0: cast[pointer](resultBinds.buffers[i].cstring) else: nil + resultBinds.binds[i].buffer_length = resultBinds.buffers[i].len.culong + resultBinds.binds[i].length = addr resultBinds.lengths[i] + resultBinds.binds[i].is_null = addr resultBinds.nullFlags[i] + resultBinds.binds[i].error = addr resultBinds.errorFlags[i] + + if cols > 0 and stmt_bind_result(stmt, resultBinds.binds[0].addr): + raise newException(DbError, $stmt_error(stmt)) + + +proc refetchTruncatedColumns( + stmt: PSTMT, + resultBinds: MariadbResultBindCache +) = + for i in 0 ..< resultBinds.binds.len: + if not resultBinds.errorFlags[i]: + continue + let needed = max(int(resultBinds.lengths[i]), resultBinds.buffers[i].len) + if needed <= 0: + continue + resultBinds.buffers[i] = newString(needed) + resultBinds.binds[i].buffer = cast[pointer](resultBinds.buffers[i].cstring) + resultBinds.binds[i].buffer_length = needed.culong + if stmt_fetch_column(stmt, resultBinds.binds[i].addr, cast[mariadb_rdb.cuint](i), 0) != 0: + raise newException(DbError, $stmt_error(stmt)) + + +proc execPreparedStmt*(conn: PMySQL, stmt: PSTMT, args: seq[PreparedParam], timeout: int) {.async.} = + assert(not conn.isNil, "Database not connected.") + let deadline = makeDeadline(timeout) + await runStmtReset(conn, stmt, deadline) + await runStmtFreeResult(conn, stmt, deadline) + bindStmtParams(stmt, args) + await runStmtExecute(conn, stmt, deadline) + await runStmtFreeResult(conn, stmt, deadline) + + +proc queryPreparedStmt*( + conn: PMySQL, + stmt: PSTMT, + args: seq[PreparedParam], + timeout: int, + resultBinds: MariadbResultBindCache +): Future[(seq[database_types.Row], DbRows)] {.async.} = + assert(not conn.isNil, "Database not connected.") + let deadline = makeDeadline(timeout) + await runStmtReset(conn, stmt, deadline) + await runStmtFreeResult(conn, stmt, deadline) + bindStmtParams(stmt, args) + await runStmtExecute(conn, stmt, deadline) + + var dbRows: DbRows + var rows = newSeq[seq[string]]() + let metadata = stmt_result_metadata(stmt) + if metadata.isNil: + await runStmtFreeResult(conn, stmt, deadline) + return (rows, dbRows) + + defer: + free_result(metadata) + await runStmtFreeResult(conn, stmt, deadline) + + await runStmtStoreResult(conn, stmt, deadline) + + let cols = int(num_fields(metadata)) + var baseColumns: DbColumns + setColumnInfo(baseColumns, metadata, cols) + bindStmtResults(stmt, metadata, resultBinds) + + while true: + let fetchRes = await runStmtFetch(conn, stmt, deadline) + if fetchRes == 100: + break + if fetchRes notin {0, 101}: + raise newException(DbError, $stmt_error(stmt)) + if fetchRes == 101: + refetchTruncatedColumns(stmt, resultBinds) + + var rowColumns = baseColumns + var row = newSeq[string](cols) + for i in 0 ..< cols: + if resultBinds.nullFlags[i]: + rowColumns[i].typ.kind = dbNull + row[i] = "" + else: + let length = min(int(resultBinds.lengths[i]), resultBinds.buffers[i].len) + if length <= 0: + row[i] = "" + else: + row[i] = resultBinds.buffers[i][0 ..< length] + rows.add(row) + dbRows.add(rowColumns) + + return (rows, dbRows) + + +proc closePreparedStmt*(stmt: PSTMT): void = + if stmt.isNil: + return + discard stmt_close(stmt) + + +proc jsonObjValuesToStrSeq(args: JsonNode): seq[string] = + result = newSeq[string](args.len) + var i = 0 + for arg in args.items: + case arg["value"].kind + of JBool: + result[i] = if arg["value"].getBool: "1" else: "0" + of JInt: + result[i] = $arg["value"].getInt + of JFloat: + result[i] = $arg["value"].getFloat + of JArray, JObject: + result[i] = arg["value"].pretty() + of JNull: + result[i] = "null" + else: + result[i] = arg["value"].getStr() + inc i + +proc jsonFlatToStrSeq(args: JsonNode): seq[string] = + result = newSeq[string](args.len) + var i = 0 + for arg in args.items: + case arg.kind + of JBool: + result[i] = if arg.getBool: "1" else: "0" + of JInt: + result[i] = $arg.getInt + of JFloat: + result[i] = $arg.getFloat + of JArray, JObject: + result[i] = arg.pretty() + of JNull: + result[i] = "null" + else: + result[i] = arg.getStr() + inc i + + +proc query*(db: PMySQL, query: string, args: seq[string], timeout: int): Future[(seq[database_types.Row], DbRows)] {.async.} = + var dbRows: DbRows + var rows = newSeq[seq[string]]() + + let q = dbFormat(query, args) + let deadline = makeDeadline(timeout) + await runRealQuery(db, q, deadline) + + let sqlres = mariadb_rdb.use_result(db) + if sqlres.isNil: + return (rows, dbRows) + + try: + let cols = int(mariadb_rdb.numFields(sqlres)) + var baseColumns: DbColumns + setColumnInfo(baseColumns, sqlres, cols) + while true: + let row = await runFetchRow(db, sqlres, deadline) + if row == nil: + break + + var baseRow = newSeq[string](cols) + var rowColumns = baseColumns + for i in 0 ..< cols: + if row[i].isNil: + rowColumns[i].typ.kind = dbNull + baseRow[i] = $row[i] + rows.add(baseRow) + dbRows.add(rowColumns) + finally: + await runFreeResult(db, sqlres, deadline) + + return (rows, dbRows) + +proc query*(db: PMySQL, query: string, args: JsonNode, timeout: int): Future[(seq[database_types.Row], DbRows)] {.async.} = + return query(db, query, jsonObjValuesToStrSeq(args), timeout).await + +proc queryPlain*(db: PMySQL, query: string, args: seq[string], timeout: int): Future[seq[database_types.Row]] {.async.} = + let q = dbFormat(query, args) + let deadline = makeDeadline(timeout) + await runRealQuery(db, q, deadline) + + var rows = newSeq[seq[string]]() + let sqlres = mariadb_rdb.use_result(db) + if sqlres.isNil: + return rows + + try: + let cols = int(mariadb_rdb.numFields(sqlres)) + while true: + let row = await runFetchRow(db, sqlres, deadline) + if row == nil: + break + + var baseRow = newSeq[string](cols) + for i in 0 ..< cols: + baseRow[i] = $row[i] + rows.add(baseRow) + finally: + await runFreeResult(db, sqlres, deadline) + + return rows + +proc queryPlain*(db: PMySQL, query: string, args: JsonNode, timeout: int): Future[seq[database_types.Row]] {.async.} = + return queryPlain(db, query, jsonFlatToStrSeq(args), timeout).await + +proc exec*(db: PMySQL, query: string, args: seq[string], timeout: int) {.async.} = + let q = dbFormat(query, args) + let deadline = makeDeadline(timeout) + await runRealQuery(db, q, deadline) + +proc exec*(db: PMySQL, query: string, args: JsonNode, timeout: int) {.async.} = + exec(db, query, jsonFlatToStrSeq(args), timeout).await + +proc exec*(db: PMySQL, query: string, args: JsonNode, columns: seq[seq[string]], timeout: int) {.async.} = + let params = MariadbParams.fromObj(args, columns) + await rawExec(db, query, params, timeout) + +proc execGetValue*(db: PMySQL, query: string, args: JsonNode, columns: seq[seq[string]], timeout: int): Future[(seq[database_types.Row], DbRows)] {.async.} = + var dbRows: DbRows + var rows = newSeq[seq[string]]() + + let params = MariadbParams.fromObj(args, columns) + let deadline = makeDeadline(timeout) + let q = dbFormat(db, query, params) + await runRealQuery(db, q, deadline) + + let sqlres = mariadb_rdb.use_result(db) + if sqlres.isNil: + return (rows, dbRows) + + try: + let cols = int(mariadb_rdb.numFields(sqlres)) + var baseColumns: DbColumns + setColumnInfo(baseColumns, sqlres, cols) + while true: + let row = await runFetchRow(db, sqlres, deadline) + if row == nil: + break + + var baseRow = newSeq[string](cols) + var rowColumns = baseColumns + for i in 0 ..< cols: + if row[i].isNil: + rowColumns[i].typ.kind = dbNull + baseRow[i] = $row[i] + rows.add(baseRow) + dbRows.add(rowColumns) + finally: + await runFreeResult(db, sqlres, deadline) + + return (rows, dbRows) + +proc rawQuery*(db: PMySQL, query: string, args: JsonNode, timeout: int): Future[(seq[database_types.Row], DbRows)] {.async.} = + return query(db, query, jsonFlatToStrSeq(args), timeout).await + +proc getColumns*(db: PMySQL, query: string, args: seq[string], timeout: int): Future[seq[string]] {.async.} = + var columns: seq[string] + let q = dbFormat(query, args) + let deadline = makeDeadline(timeout) + await runRealQuery(db, q, deadline) + + let sqlres = mariadb_rdb.use_result(db) + if sqlres.isNil: + return columns + + try: + var dbColumns: DbColumns + let cols = int(mariadb_rdb.numFields(sqlres)) + if cols <= 0: + return columns + setColumnInfo(dbColumns, sqlres, cols) + for column in dbColumns: + columns.add(column.name) + finally: + await runFreeResult(db, sqlres, deadline) + + return columns + +proc getColumnTypes*(db: PMySQL, database, table: string, timeout: int): Future[seq[database_types.Row]] {.async.} = + let sql = "SELECT `COLUMN_NAME`, `DATA_TYPE` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?" + return queryPlain(db, sql, @[database, table], timeout).await diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_lib.nim b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_lib.nim new file mode 100644 index 0000000..82552ed --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_lib.nim @@ -0,0 +1,232 @@ +import std/json +import ../../error +import ../../models/database_types +import ./mariadb_rdb + + +# type InstantRow* = object ## a handle that can be used to get a row's +# ## column text on demand +# row*: cstringArray +# len: int + +proc dbError*(db: PMySQL) {.noreturn.} = + ## raises a DbError exception. + var e: ref DbError + new(e) + e.msg = $db.error + raise e + +# proc checkError*(db: PMySQL) = +# ## Raises a DbError exception. +# var message = $db.error +# if message.len > 0: +# raise newException(DbError, $message) + +proc setTypeName(t: var DbType; f: PFIELD) = + t.name = $f.name + t.maxReprLen = Natural(f.max_length) + if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true + case f.ftype + of TYPE_DECIMAL: + t.kind = dbDecimal + of TYPE_TINY: + t.kind = dbInt + t.size = 1 + of TYPE_SHORT: + t.kind = dbInt + t.size = 2 + of TYPE_LONG: + t.kind = dbInt + t.size = 4 + of TYPE_FLOAT: + t.kind = dbFloat + t.size = 4 + of TYPE_DOUBLE: + t.kind = dbFloat + t.size = 8 + of TYPE_NULL: + t.kind = dbNull + of TYPE_TIMESTAMP: + t.kind = dbTimestamp + of TYPE_LONGLONG: + t.kind = dbInt + t.size = 8 + of TYPE_INT24: + t.kind = dbInt + t.size = 3 + of TYPE_DATE: + t.kind = dbDate + of TYPE_TIME: + t.kind = dbTime + of TYPE_DATETIME: + t.kind = dbDatetime + of TYPE_YEAR: + t.kind = dbDate + of TYPE_NEWDATE: + t.kind = dbDate + of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING: + t.kind = dbVarchar + of TYPE_BIT: + t.kind = dbBit + of TYPE_NEWDECIMAL: + t.kind = dbDecimal + of TYPE_ENUM: t.kind = dbEnum + of TYPE_SET: t.kind = dbSet + of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB, + TYPE_BLOB: t.kind = dbBlob + of TYPE_GEOMETRY: + t.kind = dbGeometry + +proc setColumnInfo*(columns: var DbColumns; res: PRES; L: int) = + setLen(columns, L) + for i in 0.. segStart: + result.add(formatstr[segStart ..< i]) + result.add(dbQuote(args[a])) + inc(a) + segStart = i + 1 + if segStart < formatstr.len: + result.add(formatstr[segStart ..< formatstr.len]) + + +# proc rawExec*(db:PMySQL, query:string, args: seq[string]) = +# var q = dbFormat(query, args) +# if realQuery(db, q.cstring, q.len) != 0'i32: dbError(db) + +# iterator instantRows*(db: PMySQL; dbRows: var DbRows; query: string; +# args: seq[string]): InstantRow = +# ## Same as fastRows but returns a handle that can be used to get column text +# ## on demand using []. Returned handle is valid only within the iterator body. +# rawExec(db, query, args) +# var sqlres = mariadb_rdb.useResult(db) +# var dbColumns: DbColumns +# if sqlres != nil: +# let L = int(mariadb_rdb.numFields(sqlres)) +# var row: cstringArray +# while true: +# setColumnInfo(dbColumns, sqlres, L) +# dbRows.add(dbColumns) +# for i in 0..L: +# row = mariadb_rdb.fetchRow(sqlres) +# if row == nil: break +# yield InstantRow(row: row, len: L) +# properFreeResult(sqlres, row) + +# proc `[]`*(row: InstantRow, col: int): string {.inline.} = +# ## Returns text for given column of the row. +# $row.row[col] + +# proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} = +# ## Return cstring of given column of the row +# row.row[index] + +# proc len*(row: InstantRow): int {.inline.} = +# ## Returns number of columns in the row. +# row.len + + +type MariadbParamValue* = object + value*: string + isBinary*:bool + +type MariadbParams* = seq[MariadbParamValue] + +proc fromObj*(_:type MariadbParams, args:JsonNode, columns:seq[seq[string]]):MariadbParams = + ## args is JArray `[{"key":"id", "value": 1}, {"key": "name" "value": "alice"}]` + result = newSeq[MariadbParamValue](args.len) + var i = 0 + for arg in args.items: + defer: i.inc() + case arg["value"].kind + of JBool: + let value = if arg["value"].getBool: "1" else: "0" + result[i] = MariadbParamValue(value:value, isBinary:false) + of JInt: + let value = $arg["value"].getInt + result[i] = MariadbParamValue(value:value, isBinary:false) + of JFloat: + let value = $arg["value"].getFloat + result[i] = MariadbParamValue(value:value, isBinary:false) + of JArray, JObject: + let value = arg["value"].pretty() + result[i] = MariadbParamValue(value:value, isBinary:false) + of JNull: + let value = "null" + result[i] = MariadbParamValue(value:value, isBinary:false) + else: # JString + let value = arg["value"].getStr + result[i] = MariadbParamValue(value:value, isBinary:false) + for column in columns: + let columnName = column[0] + let columnTyp = column[1] + if columnName == arg["key"].getStr: + defer:break + if columnTyp == "blob": + result[i].isBinary = true + + +proc dbFormat*(conn:PMySQL, query: string, args: MariadbParams): string = + result = newStringOfCap(query.len + args.len * 8) + var a = 0 + var segStart = 0 + for i in 0 ..< query.len: + if query[i] == '?': + if i > segStart: + result.add(query[segStart ..< i]) + if args[a].isBinary: + let `from` = args[a].value + var to = newString(`from`.len * 2 + 1) + let escLen = mariadb_rdb.real_escape_string(conn, to.cstring, `from`.cstring, `from`.len) + to.setLen(escLen) + result.add("\'") + result.add(to) + result.add("\'") + else: + result.add(dbQuote(args[a].value)) + inc(a) + segStart = i + 1 + if segStart < query.len: + result.add(query[segStart ..< query.len]) diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim new file mode 100644 index 0000000..ee2e845 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim @@ -0,0 +1,1220 @@ +import ../../../utils + +{.push, callconv: cdecl.} +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(unix): + when defined(macosx): + const lib = "libmariadb(|.21|.20|.19|.18|.17|.16|.15).dylib" + else: + const osName = getOsName() + when osName == "alpine": + const lib = "libmariadb.so(|.3)" + else: + # const lib = "libmysqlclient.so(|.21|.20|.19|.18|.17|.16|.15)" + const lib = "libmariadbclient.so(|.21|.20|.19|.18|.17|.16|.15)" +when defined(windows): + const lib = "libmariadb.dll" + +type + my_bool* = bool + Pmy_bool* = ptr my_bool + PVIO* = pointer +# Pgptr* = ptr gptr + gptr* = cstring +# Pmy_socket* = ptr my_socket + my_socket* = cint + PPByte* = pointer + cuint* = cint + +# type NetAsyncStatus* = enum +# NET_ASYNC_COMPLETE +# NET_ASYNC_NOT_READY +# NET_ASYNC_ERROR +# NET_ASYNC_COMPLETE_NO_MORE_RESULTS + +# # ------------ Start of declaration in "mysql_com.h" --------------------- +# # +# # ** Common definition between mysql server & client +# # +# # Field/table name length + +const + MYSQL_WAIT_READ* = 1 + MYSQL_WAIT_WRITE* = 2 + MYSQL_WAIT_EXCEPT* = 4 + MYSQL_WAIT_TIMEOUT* = 8 +# NAME_LEN* = 64 +# HOSTNAME_LENGTH* = 60 +# USERNAME_LENGTH* = 16 +# SERVER_VERSION_LENGTH* = 60 + SQLSTATE_LENGTH* = 5 +# LOCAL_HOST* = "localhost" +# LOCAL_HOST_NAMEDPIPE* = '.' + +# const +# NAMEDPIPE* = "MySQL" +# SERVICENAME* = "MySQL" + +type + Enum_server_command* {.deprecated.} = enum + COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB, + COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO, + COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME, + COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP, + COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE, + COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION, + COM_STMT_FETCH, COM_END +# # {.deprecated: "Enum_server_command".} + +const + SCRAMBLE_LENGTH* = 20 # Length of random string sent by server on handshake; +# # this is also length of obfuscated password, +# # received from client +# SCRAMBLE_LENGTH_323* = 8 # length of password stored in the db: +# # new passwords are preceded with '*' +# SCRAMBLED_PASSWORD_CHAR_LENGTH* = SCRAMBLE_LENGTH * 2 + 1 +# SCRAMBLED_PASSWORD_CHAR_LENGTH_323* = SCRAMBLE_LENGTH_323 * 2 + NOT_NULL_FLAG* = 1 # Field can't be NULL + PRI_KEY_FLAG* = 2 # Field is part of a primary key +# UNIQUE_KEY_FLAG* = 4 # Field is part of a unique key +# MULTIPLE_KEY_FLAG* = 8 # Field is part of a key +# BLOB_FLAG* = 16 # Field is a blob +# UNSIGNED_FLAG* = 32 # Field is unsigned +# ZEROFILL_FLAG* = 64 # Field is zerofill +# BINARY_FLAG* = 128 # Field is binary +# # The following are only sent to new clients +# ENUM_FLAG* = 256 # field is an enum +# AUTO_INCREMENT_FLAG* = 512 # field is a autoincrement field +# TIMESTAMP_FLAG* = 1024 # Field is a timestamp +# SET_FLAG* = 2048 # field is a set +# NO_DEFAULT_VALUE_FLAG* = 4096 # Field doesn't have default value +# NUM_FLAG* = 32768 # Field is num (for clients) +# PART_KEY_FLAG* = 16384 # Intern; Part of some key +# GROUP_FLAG* = 32768 # Intern: Group field +# UNIQUE_FLAG* = 65536 # Intern: Used by sql_yacc +# BINCMP_FLAG* = 131072 # Intern: Used by sql_yacc +# REFRESH_GRANT* = 1 # Refresh grant tables +# REFRESH_LOG* = 2 # Start on new log file +# REFRESH_TABLES* = 4 # close all tables +# REFRESH_HOSTS* = 8 # Flush host cache +# REFRESH_STATUS* = 16 # Flush status variables +# REFRESH_THREADS* = 32 # Flush thread cache +# REFRESH_SLAVE* = 64 # Reset master info and restart slave thread +# REFRESH_MASTER* = 128 # Remove all bin logs in the index and truncate the index +# # The following can't be set with mysql_refresh() +# REFRESH_READ_LOCK* = 16384 # Lock tables for read +# REFRESH_FAST* = 32768 # Intern flag +# REFRESH_QUERY_CACHE* = 65536 # RESET (remove all queries) from query cache +# REFRESH_QUERY_CACHE_FREE* = 0x00020000 # pack query cache +# REFRESH_DES_KEY_FILE* = 0x00040000 +# REFRESH_USER_RESOURCES* = 0x00080000 +# CLIENT_LONG_PASSWORD* = 1 # new more secure passwords +# CLIENT_FOUND_ROWS* = 2 # Found instead of affected rows +# CLIENT_LONG_FLAG* = 4 # Get all column flags +# CLIENT_CONNECT_WITH_DB* = 8 # One can specify db on connect +# CLIENT_NO_SCHEMA* = 16 # Don't allow database.table.column +# CLIENT_COMPRESS* = 32 # Can use compression protocol +# CLIENT_ODBC* = 64 # Odbc client +# CLIENT_LOCAL_FILES* = 128 # Can use LOAD DATA LOCAL +# CLIENT_IGNORE_SPACE* = 256 # Ignore spaces before '(' +# CLIENT_PROTOCOL_41* = 512 # New 4.1 protocol +# CLIENT_INTERACTIVE* = 1024 # This is an interactive client +# CLIENT_SSL* = 2048 # Switch to SSL after handshake +# CLIENT_IGNORE_SIGPIPE* = 4096 # IGNORE sigpipes +# CLIENT_TRANSACTIONS* = 8192 # Client knows about transactions +# CLIENT_RESERVED* = 16384 # Old flag for 4.1 protocol +# CLIENT_SECURE_CONNECTION* = 32768 # New 4.1 authentication +# CLIENT_MULTI_STATEMENTS* = 65536 # Enable/disable multi-stmt support +# CLIENT_MULTI_RESULTS* = 131072 # Enable/disable multi-results +# CLIENT_REMEMBER_OPTIONS*: int = 1 shl 31 +# SERVER_STATUS_IN_TRANS* = 1 # Transaction has started +# SERVER_STATUS_AUTOCOMMIT* = 2 # Server in auto_commit mode +# SERVER_STATUS_MORE_RESULTS* = 4 # More results on server +# SERVER_MORE_RESULTS_EXISTS* = 8 # Multi query - next query exists +# SERVER_QUERY_NO_GOOD_INDEX_USED* = 16 +# SERVER_QUERY_NO_INDEX_USED* = 32 # The server was able to fulfill the clients request and opened a +# # read-only non-scrollable cursor for a query. This flag comes +# # in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. +# SERVER_STATUS_CURSOR_EXISTS* = 64 # This flag is sent when a read-only cursor is exhausted, in reply to +# # COM_STMT_FETCH command. +# SERVER_STATUS_LAST_ROW_SENT* = 128 +# SERVER_STATUS_DB_DROPPED* = 256 # A database was dropped +# SERVER_STATUS_NO_BACKSLASH_ESCAPES* = 512 + ERRMSG_SIZE* = 200 +# NET_READ_TIMEOUT* = 30 # Timeout on read +# NET_WRITE_TIMEOUT* = 60 # Timeout on write +# NET_WAIT_TIMEOUT* = 8 * 60 * 60 # Wait for new query +# ONLY_KILL_QUERY* = 1 + +# const +# MAX_TINYINT_WIDTH* = 3 # Max width for a TINY w.o. sign +# MAX_SMALLINT_WIDTH* = 5 # Max width for a SHORT w.o. sign +# MAX_MEDIUMINT_WIDTH* = 8 # Max width for a INT24 w.o. sign +# MAX_INT_WIDTH* = 10 # Max width for a LONG w.o. sign +# MAX_BIGINT_WIDTH* = 20 # Max width for a LONGLONG +# MAX_CHAR_WIDTH* = 255 # Max length for a CHAR column +# MAX_BLOB_WIDTH* = 8192 # Default width for blob + +type +# Pst_net* = ptr St_net + St_net*{.final.} = object + vio*: PVIO + buff*: cstring + buff_end*: cstring + write_pos*: cstring + read_pos*: cstring + fd*: my_socket # For Perl DBI/dbd + max_packet*: int + max_packet_size*: int + pkt_nr*: cuint + compress_pkt_nr*: cuint + write_timeout*: cuint + read_timeout*: cuint + retry_count*: cuint + fcntl*: cint + compress*: my_bool # The following variable is set if we are doing several queries in one + # command ( as in LOAD TABLE ... FROM MASTER ), + # and do not want to confuse the client with OK at the wrong time + remain_in_buf*: int + len*: int + buf_length*: int + where_b*: int + return_status*: ptr cint + reading_or_writing*: char + save_char*: cchar + no_send_ok*: my_bool # For SPs and other things that do multiple stmts + no_send_eof*: my_bool # For SPs' first version read-only cursors + no_send_error*: my_bool # Set if OK packet is already sent, and + # we do not need to send error messages + # Pointer to query object in query cache, do not equal NULL (0) for + # queries in cache that have not stored its results yet + # $endif + last_error*: array[0..(ERRMSG_SIZE) - 1, char] + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + last_errno*: cuint + error*: char + query_cache_query*: gptr + report_error*: my_bool # We should report error (we have unreported error) + return_errno*: my_bool + + NET* = St_net + PNET* = ptr NET +# {.deprecated: "St_net, NET".} + +# const +# packet_error* = - 1 + +type + Enum_field_types* = enum # For backward compatibility + TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, + TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME, + TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT, + TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248, + TYPE_TINY_BLOB = 249, TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251, + TYPE_BLOB = 252, TYPE_VAR_STRING = 253, TYPE_STRING = 254, + TYPE_GEOMETRY = 255 +# {.deprecated: "Enum_field_types".} + +# const +# CLIENT_MULTI_QUERIES* = CLIENT_MULTI_STATEMENTS +# FIELD_TYPE_DECIMAL* = TYPE_DECIMAL +# FIELD_TYPE_NEWDECIMAL* = TYPE_NEWDECIMAL +# FIELD_TYPE_TINY* = TYPE_TINY +# FIELD_TYPE_SHORT* = TYPE_SHORT +# FIELD_TYPE_LONG* = TYPE_LONG +# FIELD_TYPE_FLOAT* = TYPE_FLOAT +# FIELD_TYPE_DOUBLE* = TYPE_DOUBLE +# FIELD_TYPE_NULL* = TYPE_NULL +# FIELD_TYPE_TIMESTAMP* = TYPE_TIMESTAMP +# FIELD_TYPE_LONGLONG* = TYPE_LONGLONG +# FIELD_TYPE_INT24* = TYPE_INT24 +# FIELD_TYPE_DATE* = TYPE_DATE +# FIELD_TYPE_TIME* = TYPE_TIME +# FIELD_TYPE_DATETIME* = TYPE_DATETIME +# FIELD_TYPE_YEAR* = TYPE_YEAR +# FIELD_TYPE_NEWDATE* = TYPE_NEWDATE +# FIELD_TYPE_ENUM* = TYPE_ENUM +# FIELD_TYPE_SET* = TYPE_SET +# FIELD_TYPE_TINY_BLOB* = TYPE_TINY_BLOB +# FIELD_TYPE_MEDIUM_BLOB* = TYPE_MEDIUM_BLOB +# FIELD_TYPE_LONG_BLOB* = TYPE_LONG_BLOB +# FIELD_TYPE_BLOB* = TYPE_BLOB +# FIELD_TYPE_VAR_STRING* = TYPE_VAR_STRING +# FIELD_TYPE_STRING* = TYPE_STRING +# FIELD_TYPE_CHAR* = TYPE_TINY +# FIELD_TYPE_INTERVAL* = TYPE_ENUM +# FIELD_TYPE_GEOMETRY* = TYPE_GEOMETRY +# FIELD_TYPE_BIT* = TYPE_BIT # Shutdown/kill enums and constants +# # Bits for THD::killable. +# SHUTDOWN_KILLABLE_CONNECT* = chr(1 shl 0) +# SHUTDOWN_KILLABLE_TRANS* = chr(1 shl 1) +# SHUTDOWN_KILLABLE_LOCK_TABLE* = chr(1 shl 2) +# SHUTDOWN_KILLABLE_UPDATE* = chr(1 shl 3) + +# type +# Enum_shutdown_level* {.deprecated.} = enum +# SHUTDOWN_DEFAULT = 0, SHUTDOWN_WAIT_CONNECTIONS = 1, +# SHUTDOWN_WAIT_TRANSACTIONS = 2, SHUTDOWN_WAIT_UPDATES = 8, +# SHUTDOWN_WAIT_ALL_BUFFERS = 16, SHUTDOWN_WAIT_CRITICAL_BUFFERS = 17, +# KILL_QUERY = 254, KILL_CONNECTION = 255 +# Enum_cursor_type* {.deprecated.} = enum # options for mysql_set_option +# CURSOR_TYPE_NO_CURSOR = 0, CURSOR_TYPE_READ_ONLY = 1, +# CURSOR_TYPE_FOR_UPDATE = 2, CURSOR_TYPE_SCROLLABLE = 4 +# Enum_mysql_set_option* {.deprecated.} = enum +# OPTION_MULTI_STATEMENTS_ON, OPTION_MULTI_STATEMENTS_OFF +# # {.deprecated: [Tenum_shutdown_level: Enum_shutdown_level, +# # Tenum_cursor_type: Enum_cursor_type, +# # Tenum_mysql_set_option: Enum_mysql_set_option].} + +# proc my_net_init*(net: PNET, vio: PVIO): my_bool{.cdecl, dynlib: lib, +# importc: "my_net_init".} +# proc my_net_local_init*(net: PNET){.cdecl, dynlib: lib, +# importc: "my_net_local_init".} +# proc net_end*(net: PNET){.cdecl, dynlib: lib, importc: "net_end".} +# proc net_clear*(net: PNET){.cdecl, dynlib: lib, importc: "net_clear".} +# proc net_realloc*(net: PNET, len: int): my_bool{.cdecl, dynlib: lib, +# importc: "net_realloc".} +# proc net_flush*(net: PNET): my_bool{.cdecl, dynlib: lib, importc: "net_flush".} +# proc my_net_write*(net: PNET, packet: cstring, length: int): my_bool{.cdecl, +# dynlib: lib, importc: "my_net_write".} +# proc net_write_command*(net: PNET, command: char, header: cstring, +# head_len: int, packet: cstring, length: int): my_bool{. +# cdecl, dynlib: lib, importc: "net_write_command".} +# proc net_real_write*(net: PNET, packet: cstring, length: int): cint{.cdecl, +# dynlib: lib, importc: "net_real_write".} +# proc my_net_read*(net: PNET): int{.cdecl, dynlib: lib, importc: "my_net_read".} +# # The following function is not meant for normal usage +# # Currently it's used internally by manager.c +# type +# Psockaddr* = ptr Sockaddr +# Sockaddr*{.deprecated, final.} = object # undefined structure +# # {.deprecated: [Tsockaddr: Sockaddr].} + +# proc my_connect*(s: my_socket, name: Psockaddr, namelen: cuint, timeout: cuint): cint{. +# cdecl, dynlib: lib, importc: "my_connect".} +# type +# Prand_struct* = ptr Rand_struct +# Rand_struct*{.final.} = object # The following is for user defined functions +# seed1*: int +# seed2*: int +# max_value*: int +# max_value_dbl*: cdouble + +# Item_result* {.deprecated.} = enum +# STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT +# PItem_result* = ptr Item_result +# Pst_udf_args* = ptr St_udf_args +# St_udf_args*{.final.} = object +# arg_count*: cuint # Number of arguments +# arg_type*: PItem_result # Pointer to item_results +# args*: cstringArray # Pointer to item_results +# lengths*: ptr int # Length of string arguments +# maybe_null*: cstring # Length of string arguments +# attributes*: cstringArray # Pointer to attribute name +# attribute_lengths*: ptr int # Length of attribute arguments + +# UDF_ARGS* {.deprecated.} = St_udf_args +# PUDF_ARGS* = ptr UDF_ARGS # This holds information about the result +# Pst_udf_init* = ptr St_udf_init +# St_udf_init*{.final.} = object +# maybe_null*: my_bool # 1 if function can return NULL +# decimals*: cuint # for real functions +# max_length*: int # For string functions +# theptr*: cstring # free pointer for function data +# const_item*: my_bool # free pointer for function data + +# UDF_INIT* {.deprecated.} = St_udf_init +# PUDF_INIT* = ptr UDF_INIT # Constants when using compression +# # {.deprecated: [Trand_stuct: Rand_struct, TItem_result: Item_result, +# # Tst_udf_args: St_udf_args, TUDF_ARGS: UDF_ARGS, +# # Tst_udf_init: St_udf_init, TUDF_INIT: UDF_INIT].} + +# const +# NET_HEADER_SIZE* = 4 # standard header size +# COMP_HEADER_SIZE* = 3 # compression header extra size +# # Prototypes to password functions +# # These functions are used for authentication by client and server and +# # implemented in sql/password.c + +# proc randominit*(para1: Prand_struct, seed1: int, seed2: int){.cdecl, +# dynlib: lib, importc: "randominit".} +# proc my_rnd*(para1: Prand_struct): cdouble{.cdecl, dynlib: lib, +# importc: "my_rnd".} +# proc create_random_string*(fto: cstring, len: cuint, rand_st: Prand_struct){. +# cdecl, dynlib: lib, importc: "create_random_string".} +# proc hash_password*(fto: int, password: cstring, password_len: cuint){.cdecl, +# dynlib: lib, importc: "hash_password".} +# proc make_scrambled_password_323*(fto: cstring, password: cstring){.cdecl, +# dynlib: lib, importc: "make_scrambled_password_323".} +# proc scramble_323*(fto: cstring, message: cstring, password: cstring){.cdecl, +# dynlib: lib, importc: "scramble_323".} +# proc check_scramble_323*(para1: cstring, message: cstring, salt: int): my_bool{. +# cdecl, dynlib: lib, importc: "check_scramble_323".} +# proc get_salt_from_password_323*(res: ptr int, password: cstring){.cdecl, +# dynlib: lib, importc: "get_salt_from_password_323".} +# proc make_password_from_salt_323*(fto: cstring, salt: ptr int){.cdecl, +# dynlib: lib, importc: "make_password_from_salt_323".} +# proc octet2hex*(fto: cstring, str: cstring, length: cuint): cstring{.cdecl, +# dynlib: lib, importc: "octet2hex".} +# proc make_scrambled_password*(fto: cstring, password: cstring){.cdecl, +# dynlib: lib, importc: "make_scrambled_password".} +# proc scramble*(fto: cstring, message: cstring, password: cstring){.cdecl, +# dynlib: lib, importc: "scramble".} +# proc check_scramble*(reply: cstring, message: cstring, hash_stage2: pointer): my_bool{. +# cdecl, dynlib: lib, importc: "check_scramble".} +# proc get_salt_from_password*(res: pointer, password: cstring){.cdecl, +# dynlib: lib, importc: "get_salt_from_password".} +# proc make_password_from_salt*(fto: cstring, hash_stage2: pointer){.cdecl, +# dynlib: lib, importc: "make_password_from_salt".} +# # end of password.c +# proc get_tty_password*(opt_message: cstring): cstring{.cdecl, dynlib: lib, +# importc: "get_tty_password".} +# proc errno_to_sqlstate*(errno: cuint): cstring{.cdecl, dynlib: lib, +# importc: "mysql_errno_to_sqlstate".} +# # Some other useful functions +# proc modify_defaults_file*(file_location: cstring, option: cstring, +# option_value: cstring, section_name: cstring, +# remove_option: cint): cint{.cdecl, dynlib: lib, +# importc: "load_defaults".} +# proc load_defaults*(conf_file: cstring, groups: cstringArray, argc: ptr cint, +# argv: ptr cstringArray): cint{.cdecl, dynlib: lib, +# importc: "load_defaults".} +# proc my_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_init".} +# proc my_thread_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_thread_init".} +# proc my_thread_end*(){.cdecl, dynlib: lib, importc: "my_thread_end".} +# const +# NULL_LENGTH*: int = int(not (0)) # For net_store_length + +# const +# STMT_HEADER* = 4 +# LONG_DATA_HEADER* = 6 # ------------ Stop of declaration in "mysql_com.h" ----------------------- +# # $include "mysql_time.h" +# # $include "mysql_version.h" +# # $include "typelib.h" +# # $include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */ +# # var +# # mysql_port : cuint;cvar;external; +# # mysql_unix_port : Pchar;cvar;external; + +# const +# CLIENT_NET_READ_TIMEOUT* = 365 * 24 * 3600 # Timeout on read +# CLIENT_NET_WRITE_TIMEOUT* = 365 * 24 * 3600 # Timeout on write + +type +# Pst_mysql_field* = ptr St_mysql_field + St_mysql_field*{.final.} = object + name*: cstring # Name of column + org_name*: cstring # Original column name, if an alias + table*: cstring # Table of column if column was a field + org_table*: cstring # Org table name, if table was an alias + db*: cstring # Database for table + catalog*: cstring # Catalog for table + def*: cstring # Default value (set by mysql_list_fields) + len*: int # Width of column (create length) + max_length*: int # Max width for selected set + name_length*: cuint + org_name_length*: cuint + table_length*: cuint + org_table_length*: cuint + db_length*: cuint + catalog_length*: cuint + def_length*: cuint + flags*: cuint # Div flags + decimals*: cuint # Number of decimals in field + charsetnr*: cuint # Character set + ftype*: Enum_field_types # Type of field. See mysql_com.h for types + extension*: pointer + + FIELD* = St_mysql_field + PFIELD* = ptr FIELD +# PROW* = ptr ROW # return data as array of strings + ROW* = cstringArray +# PFIELD_OFFSET* = ptr FIELD_OFFSET # offset to current field +# FIELD_OFFSET* = cuint +# {.deprecated: [Tst_mysql_field: St_mysql_field, TFIELD: FIELD, TROW: ROW, +# TFIELD_OFFSET: FIELD_OFFSET].} + +# proc IS_PRI_KEY*(n: int32): bool +# proc IS_NOT_NULL*(n: int32): bool +# proc IS_BLOB*(n: int32): bool +# proc IS_NUM*(t: Enum_field_types): bool +# proc INTERNAL_NUM_FIELD*(f: Pst_mysql_field): bool +# proc IS_NUM_FIELD*(f: Pst_mysql_field): bool +type + my_ulonglong* = int64 +# Pmy_ulonglong* = ptr my_ulonglong + +# const +# COUNT_ERROR* = not (my_ulonglong(0)) + +type + Pst_mysql_rows* = ptr St_mysql_rows + St_mysql_rows*{.final.} = object + next*: Pst_mysql_rows # list of rows + data*: ROW + len*: int + + ROWS* = St_mysql_rows + PROWS* = ptr ROWS +# PROW_OFFSET* = ptr ROW_OFFSET # offset to current row +# ROW_OFFSET* = ROWS +# {.deprecated: [Tst_mysql_rows: St_mysql_rows, TROWS: ROWS, +# TROW_OFFSET: ROW_OFFSET].} + +# const +# ALLOC_MAX_BLOCK_TO_DROP* = 4096 +# ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP* = 10 # struct for once_alloc (block) + +type + Pst_used_mem* = ptr St_used_mem + St_used_mem*{.final.} = object + next*: Pst_used_mem # Next block in use + left*: cuint # memory left in block + size*: cuint # size of block + + USED_MEM* = St_used_mem + PUSED_MEM* = ptr USED_MEM +# Pst_mem_root* = ptr St_mem_root + St_mem_root*{.final.} = object + free*: PUSED_MEM # blocks with free memory in it + used*: PUSED_MEM # blocks almost without free memory + pre_alloc*: PUSED_MEM # preallocated block + min_malloc*: cuint # if block have less memory it will be put in 'used' list + block_size*: cuint # initial block size + block_num*: cuint # allocated blocks counter + # first free block in queue test counter (if it exceed + # MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list) + first_block_usage*: cuint + error_handler*: proc (){.cdecl.} + + MEM_ROOT* = St_mem_root +# PMEM_ROOT* = ptr MEM_ROOT # ------------ Stop of declaration in "my_alloc.h" ---------------------- +# {.deprecated: [Tst_used_mem: St_used_mem, TUSED_MEM: USED_MEM, +# Tst_mem_root: St_mem_root, TMEM_ROOT: MEM_ROOT].} + +type +# Pst_mysql_data* = ptr St_mysql_data + St_mysql_data*{.final.} = object + rows*: my_ulonglong + fields*: cuint + data*: PROWS + alloc*: MEM_ROOT + prev_ptr*: ptr PROWS + + DATA* = St_mysql_data + PDATA* = ptr DATA + Option* = enum + OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND, + READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME, + OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT, + OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION, + OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP, + SECURE_AUTH, REPORT_DATA_TRUNCATION, OPT_RECONNECT, + MYSQL_OPT_NONBLOCK = 6000 +# {.deprecated: [Tst_mysql_data: St_mysql_data, TDATA: DATA, Toption: Option].} + +# const +# MAX_MYSQL_MANAGER_ERR* = 256 +# MAX_MYSQL_MANAGER_MSG* = 256 +# MANAGER_OK* = 200 +# MANAGER_INFO* = 250 +# MANAGER_ACCESS* = 401 +# MANAGER_CLIENT_ERR* = 450 +# MANAGER_INTERNAL_ERR* = 500 + +type + St_dynamic_array*{.final.} = object + buffer*: cstring + elements*: cuint + max_element*: cuint + alloc_increment*: cuint + size_of_element*: cuint + +# DYNAMIC_ARRAY* = St_dynamic_array + Pst_dynamic_array* = ptr St_dynamic_array +# Pst_mysql_options* = ptr St_mysql_options + St_mysql_options*{.final.} = object + connect_timeout*: cuint + read_timeout*: cuint + write_timeout*: cuint + port*: cuint + protocol*: cuint + client_flag*: int + host*: cstring + user*: cstring + password*: cstring + unix_socket*: cstring + db*: cstring + init_commands*: Pst_dynamic_array + my_cnf_file*: cstring + my_cnf_group*: cstring + charset_dir*: cstring + charset_name*: cstring + ssl_key*: cstring # PEM key file + ssl_cert*: cstring # PEM cert file + ssl_ca*: cstring # PEM CA file + ssl_capath*: cstring # PEM directory of CA-s? + ssl_cipher*: cstring # cipher to use + shared_memory_base_name*: cstring + max_allowed_packet*: int + use_ssl*: my_bool # if to use SSL or not + compress*: my_bool + named_pipe*: my_bool # On connect, find out the replication role of the server, and + # establish connections to all the peers + rpl_probe*: my_bool # Each call to mysql_real_query() will parse it to tell if it is a read + # or a write, and direct it to the slave or the master + rpl_parse*: my_bool # If set, never read from a master, only from slave, when doing + # a read that is replication-aware + no_master_reads*: my_bool + separate_thread*: my_bool + methods_to_use*: Option + client_ip*: cstring + secure_auth*: my_bool # Refuse client connecting to server if it uses old (pre-4.1.1) protocol + report_data_truncation*: my_bool # 0 - never report, 1 - always report (default) + # function pointers for local infile support + local_infile_init*: proc (para1: var pointer, para2: cstring, para3: pointer): cint{. + cdecl.} + local_infile_read*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_end*: proc (para1: pointer) + local_infile_error*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_userdata*: pointer + + Status* = enum + STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT +# Protocol_type* = enum # There are three types of queries - the ones that have to go to +# # the master, the ones that go to a slave, and the administrative +# # type which must happen on the pivot connection +# PROTOCOL_DEFAULT, PROTOCOL_TCP, PROTOCOL_SOCKET, PROTOCOL_PIPE, +# PROTOCOL_MEMORY +# Rpl_type* = enum +# RPL_MASTER, RPL_SLAVE, RPL_ADMIN + Charset_info_st*{.final.} = object + number*: cuint + primary_number*: cuint + binary_number*: cuint + state*: cuint + csname*: cstring + name*: cstring + comment*: cstring + tailoring*: cstring + ftype*: cstring + to_lower*: cstring + to_upper*: cstring + sort_order*: cstring + contractions*: ptr int16 + sort_order_big*: ptr ptr int16 + tab_to_uni*: ptr int16 + tab_from_uni*: pointer # was ^MY_UNI_IDX + state_map*: cstring + ident_map*: cstring + strxfrm_multiply*: cuint + mbminlen*: cuint + mbmaxlen*: cuint + min_sort_char*: int16 + max_sort_char*: int16 + escape_with_backslash_is_dangerous*: my_bool + cset*: pointer # was ^MY_CHARSET_HANDLER + coll*: pointer # was ^MY_COLLATION_HANDLER; + +# CHARSET_INFO* = Charset_info_st + Pcharset_info_st* = ptr Charset_info_st +# Pcharacter_set* = ptr Character_set +# Character_set*{.final.} = object +# number*: cuint +# state*: cuint +# csname*: cstring +# name*: cstring +# comment*: cstring +# dir*: cstring +# mbminlen*: cuint +# mbmaxlen*: cuint + +# MY_CHARSET_INFO* = Character_set +# PMY_CHARSET_INFO* = ptr MY_CHARSET_INFO + Pst_mysql_methods* = ptr St_mysql_methods + Pst_mysql* = ptr St_mysql + St_mysql*{.final.} = object + net*: NET # Communication parameters + connector_fd*: gptr # ConnectorFd for SSL + host*: cstring + user*: cstring + passwd*: cstring + unix_socket*: cstring + server_version*: cstring + host_info*: cstring + info*: cstring + db*: cstring + charset*: Pcharset_info_st + fields*: PFIELD + field_alloc*: MEM_ROOT + affected_rows*: my_ulonglong + insert_id*: my_ulonglong # id if insert on table with NEXTNR + extra_info*: my_ulonglong # Used by mysqlshow, not used by mysql 5.0 and up + thread_id*: int # Id for connection in server + packet_length*: int + port*: cuint + client_flag*: int + server_capabilities*: int + protocol_version*: cuint + field_count*: cuint + server_status*: cuint + server_language*: cuint + warning_count*: cuint + options*: St_mysql_options + status*: Status + free_me*: my_bool # If free in mysql_close + reconnect*: my_bool # set to 1 if automatic reconnect + scramble*: array[0..(SCRAMBLE_LENGTH + 1) - 1, char] # session-wide random string + # Set if this is the original connection, not a master or a slave we have + # added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave() + rpl_pivot*: my_bool # Pointers to the master, and the next slave connections, points to + # itself if lone connection. + master*: Pst_mysql + next_slave*: Pst_mysql + last_used_slave*: Pst_mysql # needed for round-robin slave pick + last_used_con*: Pst_mysql # needed for send/read/store/use result to work correctly with replication + stmts*: pointer # was PList, list of all statements + methods*: Pst_mysql_methods + thd*: pointer # Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag + # from mysql_stmt_close if close had to cancel result set of this object. + unbuffered_fetch_owner*: Pmy_bool + + MySQL* = St_mysql + PMySQL* = ptr MySQL +# Pst_mysql_res* = ptr St_mysql_res + St_mysql_res*{.final.} = object + row_count*: my_ulonglong + fields*: PFIELD + data*: PDATA + data_cursor*: PROWS + lengths*: ptr int # column lengths of current row + handle*: PMySQL # for unbuffered reads + field_alloc*: MEM_ROOT + field_count*: cuint + current_field*: cuint + row*: ROW # If unbuffered read + current_row*: ROW # buffer to current row + eof*: my_bool # Used by mysql_fetch_row + unbuffered_fetch_cancelled*: my_bool # mysql_stmt_close() had to cancel this result + methods*: Pst_mysql_methods + + RES* = St_mysql_res + PRES* = ptr RES + Pst_mysql_stmt* = ptr St_mysql_stmt + PSTMT* = ptr STMT + St_mysql_methods*{.final.} = object + read_query_result*: proc (MySQL: PMySQL): my_bool{.cdecl.} + advanced_command*: proc (MySQL: PMySQL, command: Enum_server_command, header: cstring, + header_length: int, arg: cstring, arg_length: int, + skip_check: my_bool): my_bool + read_rows*: proc (MySQL: PMySQL, fields: PFIELD, fields_count: cuint): PDATA + use_result*: proc (MySQL: PMySQL): PRES + fetch_lengths*: proc (fto: ptr int, column: ROW, field_count: cuint) + flush_use_result*: proc (MySQL: PMySQL) + list_fields*: proc (MySQL: PMySQL): PFIELD + read_prepare_result*: proc (MySQL: PMySQL, stmt: PSTMT): my_bool + stmt_execute*: proc (stmt: PSTMT): cint + read_binary_rows*: proc (stmt: PSTMT): cint + unbuffered_fetch*: proc (MySQL: PMySQL, row: cstringArray): cint + free_embedded_thd*: proc (MySQL: PMySQL) + read_statistics*: proc (MySQL: PMySQL): cstring + next_result*: proc (MySQL: PMySQL): my_bool + read_change_user_result*: proc (MySQL: PMySQL, buff: cstring, passwd: cstring): cint + read_rowsfrom_cursor*: proc (stmt: PSTMT): cint + +# METHODS* = St_mysql_methods +# PMETHODS* = ptr METHODS +# Pst_mysql_manager* = ptr St_mysql_manager +# St_mysql_manager*{.final.} = object +# net*: NET +# host*: cstring +# user*: cstring +# passwd*: cstring +# port*: cuint +# free_me*: my_bool +# eof*: my_bool +# cmd_status*: cint +# last_errno*: cint +# net_buf*: cstring +# net_buf_pos*: cstring +# net_data_end*: cstring +# net_buf_size*: cint +# last_error*: array[0..(MAX_MYSQL_MANAGER_ERR) - 1, char] + +# MANAGER* = St_mysql_manager +# PMANAGER* = ptr MANAGER +# Pst_mysql_parameters* = ptr St_mysql_parameters +# St_mysql_parameters*{.final.} = object +# p_max_allowed_packet*: ptr int +# p_net_buffer_length*: ptr int + +# PARAMETERS* = St_mysql_parameters +# PPARAMETERS* = ptr PARAMETERS + Enum_mysql_stmt_state* = enum + STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE + # Pst_mysql_bind* = ptr St_mysql_bind +# # St_mysql_bind*{.final.} = object +# # len*: int # output length pointer +# # is_null*: Pmy_bool # Pointer to null indicator +# # buffer*: pointer # buffer to get/put data +# # error*: Pmy_bool # set this if you want to track data truncations happened during fetch +# # buffer_type*: Enum_field_types # buffer type +# # buffer_length*: int # buffer length, must be set for str/binary +# # # Following are for internal use. Set by mysql_stmt_bind_param +# # row_ptr*: ptr byte # for the current data position +# # offset*: int # offset position for char/binary fetch +# # length_value*: int # Used if length is 0 +# # param_number*: cuint # For null count and error messages +# # pack_length*: cuint # Internal length for packed data +# # error_value*: my_bool # used if error is 0 +# # is_unsigned*: my_bool # set if integer type is unsigned +# # long_data_used*: my_bool # If used with mysql_send_long_data +# # is_null_value*: my_bool # Used if is_null is 0 +# # store_param_func*: proc (net: PNET, param: Pst_mysql_bind){.cdecl.} +# # fetch_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) +# # skip_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) + + U* {.final, union, pure.} = object # union for row_ptr and indicator +# row_ptr*: ptr byte # for the current data position +# indicator*: cstring + + St_mysql_bind*{.final.} = object + length*: ptr culong # output length pointer + is_null*: Pmy_bool # Pointer to null indicator + buffer*: pointer # buffer to get/put data + error*: Pmy_bool # set this if you want to track data truncations happened during fetch + u*: U + store_param_func*: proc (net: PNET, param: ptr St_mysql_bind){.cdecl.} + fetch_result*: proc (param: ptr St_mysql_bind, field: PFIELD, row: PPByte){.cdecl.} + skip_result*: proc (param: ptr St_mysql_bind, field: PFIELD, row: PPByte){.cdecl.} + buffer_length*: culong # buffer length, must be set when fetching str/binary + offset*: culong # offset position for char/binary fetch + length_value*: culong # Used if length is 0 + flags*: cuint # special flags, e.g. for dummy bind + pack_length*: cuint # Internal length for packed data + buffer_type*: Enum_field_types # buffer type + error_value*: my_bool # used if error is 0 + is_unsigned*: my_bool # set if integer type is unsigned + long_data_used*: my_bool # If used with mysql_send_long_data + is_null_value*: my_bool # Used if is_null is 0 + extension*: pointer + + BIND* = St_mysql_bind + PBIND* = ptr BIND # statement handler + St_mysql_stmt*{.final.} = object + mem_root*: MEM_ROOT # root allocations + mysql*: PMySQL # connection handle + params*: PBIND # input parameters + `bind`*: PBIND # input parameters + fields*: PFIELD # result set metadata + result*: DATA # cached result set + data_cursor*: PROWS # current row in cached result + affected_rows*: my_ulonglong # copy of mysql->affected_rows after statement execution + insert_id*: my_ulonglong + read_row_func*: proc (stmt: Pst_mysql_stmt, row: PPByte): cint{.cdecl.} + stmt_id*: int # Id for prepared statement + flags*: int # i.e. type of cursor to open + prefetch_rows*: int # number of rows per one COM_FETCH + server_status*: cuint # Copied from mysql->server_status after execute/fetch to know + # server-side cursor status for this statement. + last_errno*: cuint # error code + param_count*: cuint # input parameter count + field_count*: cuint # number of columns in result set + state*: Enum_mysql_stmt_state # statement state + last_error*: array[0..(ERRMSG_SIZE) - 1, char] # error message + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + send_types_to_server*: my_bool # Types of input parameters should be sent to server + bind_param_done*: my_bool # input buffers were supplied + bind_result_done*: char # output buffers were supplied + unbuffered_fetch_cancelled*: my_bool + update_max_length*: my_bool + + STMT* = St_mysql_stmt + + Enum_stmt_attr_type* = enum + STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS +# {.deprecated: [Tst_dynamic_array: St_dynamic_array, Tst_mysql_options: St_mysql_options, +# TDYNAMIC_ARRAY: DYNAMIC_ARRAY, Tprotocol_type: Protocol_type, +# Trpl_type: Rpl_type, Tcharset_info_st: Charset_info_st, +# TCHARSET_INFO: CHARSET_INFO, Tcharacter_set: Character_set, +# TMY_CHARSET_INFO: MY_CHARSET_INFO, Tst_mysql: St_mysql, +# Tst_mysql_methods: St_mysql_methods, TMySql: MySql, +# Tst_mysql_res: St_mysql_res, TMETHODS: METHODS, TRES: RES, +# Tst_mysql_manager: St_mysql_manager, TMANAGER: MANAGER, +# Tst_mysql_parameters: St_mysql_parameters, TPARAMETERS: PARAMETERS, +# Tenum_mysql_stmt_state: Enum_mysql_stmt_state, +# Tst_mysql_bind: St_mysql_bind, TBIND: BIND, Tst_mysql_stmt: St_mysql_stmt, +# TSTMT: STMT, Tenum_stmt_attr_type: Enum_stmt_attr_type, +# Tstatus: Status].} + +# proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. +# cdecl, dynlib: lib, importc: "mysql_server_init".} +# proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} +# # mysql_server_init/end need to be called when using libmysqld or +# # libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so +# # you don't need to call it explicitly; but you need to call +# # mysql_server_end() to free memory). The names are a bit misleading +# # (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general +# # names which suit well whether you're using libmysqld or libmysqlclient. We +# # intend to promote these aliases over the mysql_server* ones. +# proc library_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. +# cdecl, dynlib: lib, importc: "mysql_server_init".} +# proc library_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} +# proc get_parameters*(): PPARAMETERS{.stdcall, dynlib: lib, +# importc: "mysql_get_parameters".} +# # Set up and bring down a thread; these function should be called +# # for each thread in an application which opens at least one MySQL +# # connection. All uses of the connection(s) should be between these +# # function calls. +# proc thread_init*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_thread_init".} +# proc thread_end*(){.stdcall, dynlib: lib, importc: "mysql_thread_end".} +# # Functions to get information from the MYSQL and MYSQL_RES structures +# # Should definitely be used if one uses shared libraries. +# proc num_rows*(res: PRES): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_num_rows".} +proc num_fields*(res: PRES): cuint{.stdcall, dynlib: lib, + importc: "mysql_num_fields".} +# proc eof*(res: PRES): my_bool{.stdcall, dynlib: lib, importc: "mysql_eof".} +proc fetch_field_direct*(res: PRES, fieldnr: cuint): PFIELD{.stdcall, + dynlib: lib, importc: "mysql_fetch_field_direct".} +# proc fetch_fields*(res: PRES): PFIELD{.stdcall, dynlib: lib, +# importc: "mysql_fetch_fields".} +# proc row_tell*(res: PRES): ROW_OFFSET{.stdcall, dynlib: lib, +# importc: "mysql_row_tell".} +# proc field_tell*(res: PRES): FIELD_OFFSET{.stdcall, dynlib: lib, +# importc: "mysql_field_tell".} +# proc field_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, +# importc: "mysql_field_count".} +# proc affected_rows*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_affected_rows".} +# proc insert_id*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_insert_id".} +# proc errno*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, importc: "mysql_errno".} +proc error*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_error".} +# proc sqlstate*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_sqlstate".} +# proc warning_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, +# importc: "mysql_warning_count".} +# proc info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_info".} +# proc thread_id*(MySQL: PMySQL): int{.stdcall, dynlib: lib, importc: "mysql_thread_id".} +# proc character_set_name*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, +# importc: "mysql_character_set_name".} +# proc set_character_set*(MySQL: PMySQL, csname: cstring): int32{.stdcall, dynlib: lib, +# importc: "mysql_set_character_set".} +proc init*(MySQL: PMySQL): PMySQL{.stdcall, dynlib: lib, importc: "mysql_init".} +proc init*(MySQL: var PMySQL){.stdcall, dynlib: lib, importc: "mysql_init".} +# proc ssl_set*(MySQL: PMySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring, +# cipher: cstring): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_ssl_set".} +# proc change_user*(MySQL: PMySQL, user: cstring, passwd: cstring, db: cstring): my_bool{. +# stdcall, dynlib: lib, importc: "mysql_change_user".} +proc real_connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: int): PMySQL{.stdcall, dynlib: lib, + importc: "mysql_real_connect".} +# proc real_connect_start*(ret: ptr PMySQL, mysql: PMySQL, host: cstring, user: cstring, passwd: cstring, +# db: cstring, port: cuint, unix_socket: cstring, +# clientflag: culong): cint{.stdcall, dynlib: lib, +# importc: "mysql_real_connect_start".} +# proc fetch_row_nonblocking*(MYSQL_RES: PRES, MYSQL_ROW: PROW): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_fetch_row_nonblocking".} +# proc free_result_nonblocking*(MYSQL_RES: PRES): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_free_result_nonblocking".} +# proc fetch_next_result_nonblocking*(MySQL: PMySQL): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_next_result_nonblocking".} +# proc real_query_nonblocking*(MySQL: PMySQL, q: cstring, length: int): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_real_query_nonblocking".} +# proc store_result_nonblocking*(MySQL: PMySQL, MYSQL_RES: ptr PRES): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_store_result_nonblocking".} +# proc select_db*(MySQL: PMySQL, db: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_select_db".} +# proc query*(MySQL: PMySQL, q: cstring): cint{.stdcall, dynlib: lib, importc: "mysql_query".} +# proc send_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, +# importc: "mysql_send_query".} +proc real_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_real_query".} +# proc store_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, +# importc: "mysql_store_result".} +proc use_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, importc: "mysql_use_result".} +# # perform query on master +# proc master_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_master_query".} +# proc master_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, +# dynlib: lib, importc: "mysql_master_send_query".} +# # perform query on slave +# proc slave_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_slave_query".} +# proc slave_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, +# dynlib: lib, importc: "mysql_slave_send_query".} +# proc get_character_set_info*(MySQL: PMySQL, charset: PMY_CHARSET_INFO){.stdcall, +# dynlib: lib, importc: "mysql_get_character_set_info".} +# # local infile support +# const +# LOCAL_INFILE_ERROR_LEN* = 512 + +# # procedure mysql_set_local_infile_handler(mysql:PMYSQL; local_infile_init:function (para1:Ppointer; para2:Pchar; para3:pointer):longint; local_infile_read:function (para1:pointer; para2:Pchar; para3:dword):longint; local_infile_end:procedure (_pa +# # para6:pointer);cdecl;external mysqllib name 'mysql_set_local_infile_handler'; + +# proc set_local_infile_default*(MySQL: PMySQL){.cdecl, dynlib: lib, +# importc: "mysql_set_local_infile_default".} +# # enable/disable parsing of all queries to decide if they go on master or +# # slave +# proc enable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, +# importc: "mysql_enable_rpl_parse".} +# proc disable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, +# importc: "mysql_disable_rpl_parse".} +# # get the value of the parse flag +# proc rpl_parse_enabled*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, +# importc: "mysql_rpl_parse_enabled".} +# # enable/disable reads from master +# proc enable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, +# importc: "mysql_enable_reads_from_master".} +# proc disable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, importc: "mysql_disable_reads_from_master".} +# # get the value of the master read flag +# proc reads_from_master_enabled*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_reads_from_master_enabled".} +# proc rpl_query_type*(q: cstring, length: cint): Rpl_type{.stdcall, dynlib: lib, +# importc: "mysql_rpl_query_type".} +# # discover the master and its slaves +# proc rpl_probe*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rpl_probe".} +# # set the master, close/free the old one, if it is not a pivot +# proc set_master*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. +# stdcall, dynlib: lib, importc: "mysql_set_master".} +# proc add_slave*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. +# stdcall, dynlib: lib, importc: "mysql_add_slave".} +# proc shutdown*(MySQL: PMySQL, shutdown_level: Enum_shutdown_level): cint{.stdcall, +# dynlib: lib, importc: "mysql_shutdown".} +# proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, +# importc: "mysql_dump_debug_info".} +# proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib, +# importc: "mysql_refresh".} +# proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".} +# proc set_server_option*(MySQL: PMySQL, option: Enum_mysql_set_option): cint{.stdcall, +# dynlib: lib, importc: "mysql_set_server_option".} +proc ping*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_ping".} +# proc stat*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_stat".} +# proc get_server_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, +# importc: "mysql_get_server_info".} +# proc get_client_info*(): cstring{.stdcall, dynlib: lib, +# importc: "mysql_get_client_info".} +# proc get_client_version*(): int{.stdcall, dynlib: lib, +# importc: "mysql_get_client_version".} +# proc get_host_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, +# importc: "mysql_get_host_info".} +# proc get_server_version*(MySQL: PMySQL): int{.stdcall, dynlib: lib, +# importc: "mysql_get_server_version".} +# proc get_proto_info*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, +# importc: "mysql_get_proto_info".} +# proc list_dbs*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, +# importc: "mysql_list_dbs".} +# proc list_tables*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, +# importc: "mysql_list_tables".} +# proc list_processes*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, +# importc: "mysql_list_processes".} +# proc options*(MySQL: PMySQL, option: Option, arg: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_options".} +proc options*(MySQL: PMySQL, option: Option, arg: pointer): cint{.stdcall, dynlib: lib, + importc: "mysql_options".} +proc optionsv*(MySQL: PMySQL, option: Option): cint{.cdecl, dynlib: lib, + importc: "mysql_optionsv", varargs.} +proc get_socket*(MySQL: PMySQL): my_socket{.stdcall, dynlib: lib, + importc: "mysql_get_socket".} +proc get_timeout_value*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_get_timeout_value".} +proc get_timeout_value_ms*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_get_timeout_value_ms".} +proc free_result*(result: PRES){.stdcall, dynlib: lib, + importc: "mysql_free_result".} +proc free_result_start*(result: PRES): cint{.stdcall, dynlib: lib, + importc: "mysql_free_result_start".} +proc free_result_cont*(result: PRES, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_free_result_cont".} +# proc data_seek*(result: PRES, offset: my_ulonglong){.stdcall, dynlib: lib, +# importc: "mysql_data_seek".} +# proc row_seek*(result: PRES, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, +# dynlib: lib, importc: "mysql_row_seek".} +# proc field_seek*(result: PRES, offset: FIELD_OFFSET): FIELD_OFFSET{.stdcall, +# dynlib: lib, importc: "mysql_field_seek".} +proc fetch_row*(result: PRES): ROW{.stdcall, dynlib: lib, + importc: "mysql_fetch_row".} +proc fetch_row_start*(ret: ptr ROW, result: PRES): cint{.stdcall, dynlib: lib, + importc: "mysql_fetch_row_start".} +proc fetch_row_cont*(ret: ptr ROW, result: PRES, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_fetch_row_cont".} +# proc fetch_lengths*(result: PRES): ptr int{.stdcall, dynlib: lib, +# importc: "mysql_fetch_lengths".} +# proc fetch_field*(result: PRES): PFIELD{.stdcall, dynlib: lib, +# importc: "mysql_fetch_field".} +# proc list_fields*(MySQL: PMySQL, table: cstring, wild: cstring): PRES{.stdcall, +# dynlib: lib, importc: "mysql_list_fields".} +# proc escape_string*(fto: cstring, `from`: cstring, from_length: int): int{. +# stdcall, dynlib: lib, importc: "mysql_escape_string".} +# proc hex_string*(fto: cstring, `from`: cstring, from_length: int): int{.stdcall, +# dynlib: lib, importc: "mysql_hex_string".} +proc real_escape_string*(MySQL: PMySQL, fto: cstring, `from`: cstring, len: int): int{. + stdcall, dynlib: lib, importc: "mysql_real_escape_string".} +# proc debug*(debug: cstring){.stdcall, dynlib: lib, importc: "mysql_debug".} +# # function mysql_odbc_escape_string(mysql:PMYSQL; fto:Pchar; to_length:dword; from:Pchar; from_length:dword; +# # param:pointer; extend_buffer:function (para1:pointer; to:Pchar; length:Pdword):Pchar):Pchar;stdcall;external mysqllib name 'mysql_odbc_escape_string'; +# proc myodbc_remove_escape*(MySQL: PMySQL, name: cstring){.stdcall, dynlib: lib, +# importc: "myodbc_remove_escape".} +# proc thread_safe*(): cuint{.stdcall, dynlib: lib, importc: "mysql_thread_safe".} +# proc embedded*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_embedded".} +# proc manager_init*(con: PMANAGER): PMANAGER{.stdcall, dynlib: lib, +# importc: "mysql_manager_init".} +# proc manager_connect*(con: PMANAGER, host: cstring, user: cstring, +# passwd: cstring, port: cuint): PMANAGER{.stdcall, +# dynlib: lib, importc: "mysql_manager_connect".} +# proc manager_close*(con: PMANAGER){.stdcall, dynlib: lib, +# importc: "mysql_manager_close".} +# proc manager_command*(con: PMANAGER, cmd: cstring, cmd_len: cint): cint{. +# stdcall, dynlib: lib, importc: "mysql_manager_command".} +# proc manager_fetch_line*(con: PMANAGER, res_buf: cstring, res_buf_size: cint): cint{. +# stdcall, dynlib: lib, importc: "mysql_manager_fetch_line".} +# proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_read_query_result".} +proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_read_query_result".} +proc read_query_result_start*(ret: ptr my_bool, MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_read_query_result_start".} +proc read_query_result_cont*(ret: ptr my_bool, MySQL: PMySQL, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_read_query_result_cont".} +proc real_connect_start*(ret: ptr PMySQL, MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: culong): cint{.stdcall, dynlib: lib, + importc: "mysql_real_connect_start".} +proc real_connect_cont*(ret: ptr PMySQL, MySQL: PMySQL, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_real_connect_cont".} +proc real_query_start*(ret: ptr cint, MySQL: PMySQL, q: cstring, len: culong): cint{.stdcall, dynlib: lib, + importc: "mysql_real_query_start".} +proc real_query_cont*(ret: ptr cint, MySQL: PMySQL, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_real_query_cont".} +proc store_result_start*(ret: ptr PRES, MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_store_result_start".} +proc store_result_cont*(ret: ptr PRES, MySQL: PMySQL, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_store_result_cont".} +proc stmt_init*(MySQL: PMySQL): PSTMT{.stdcall, dynlib: lib, importc: "mysql_stmt_init".} +proc stmt_prepare_start*(ret: ptr cint, stmt: PSTMT, query: cstring, len: culong): cint{.stdcall, + dynlib: lib, importc: "mysql_stmt_prepare_start".} +proc stmt_prepare_cont*(ret: ptr cint, stmt: PSTMT, status: cint): cint{.stdcall, + dynlib: lib, importc: "mysql_stmt_prepare_cont".} +proc stmt_execute_start*(ret: ptr cint, stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_execute_start".} +proc stmt_execute_cont*(ret: ptr cint, stmt: PSTMT, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_execute_cont".} +proc stmt_fetch_start*(ret: ptr cint, stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_fetch_start".} +proc stmt_fetch_cont*(ret: ptr cint, stmt: PSTMT, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_fetch_cont".} +proc stmt_fetch_column*(stmt: PSTMT, `bind`: PBIND, column: cuint, offset: int): cint{. + stdcall, dynlib: lib, importc: "mysql_stmt_fetch_column".} +proc stmt_store_result_start*(ret: ptr cint, stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_store_result_start".} +proc stmt_store_result_cont*(ret: ptr cint, stmt: PSTMT, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_store_result_cont".} +proc stmt_param_count*(stmt: PSTMT): int{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_count".} +proc stmt_attr_set*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_set".} +proc stmt_attr_get*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_get".} +proc stmt_bind_param*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_param".} +proc stmt_bind_result*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_result".} +proc stmt_close*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_close".} +proc stmt_reset*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_reset".} +proc stmt_free_result*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_free_result".} +proc stmt_reset_start*(ret: ptr my_bool, stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_reset_start".} +proc stmt_reset_cont*(ret: ptr my_bool, stmt: PSTMT, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_reset_cont".} +proc stmt_free_result_start*(ret: ptr my_bool, stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_free_result_start".} +proc stmt_free_result_cont*(ret: ptr my_bool, stmt: PSTMT, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_free_result_cont".} +proc stmt_close_start*(ret: ptr my_bool, stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_close_start".} +proc stmt_close_cont*(ret: ptr my_bool, stmt: PSTMT, status: cint): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_close_cont".} +proc stmt_result_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_result_metadata".} +proc stmt_errno*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, + importc: "mysql_stmt_errno".} +proc stmt_error*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_error".} +proc stmt_sqlstate*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_sqlstate".} +# proc stmt_send_long_data*(stmt: PSTMT, param_number: cuint, data: cstring, +# len: int): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_stmt_send_long_data".} +# proc stmt_param_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, +# importc: "mysql_stmt_param_metadata".} +# proc stmt_row_seek*(stmt: PSTMT, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, +# dynlib: lib, importc: "mysql_stmt_row_seek".} +# proc stmt_row_tell*(stmt: PSTMT): ROW_OFFSET{.stdcall, dynlib: lib, +# importc: "mysql_stmt_row_tell".} +# proc stmt_data_seek*(stmt: PSTMT, offset: my_ulonglong){.stdcall, dynlib: lib, +# importc: "mysql_stmt_data_seek".} +# proc stmt_num_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_stmt_num_rows".} +# proc stmt_affected_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_stmt_affected_rows".} +# proc stmt_insert_id*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_stmt_insert_id".} +# proc stmt_field_count*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, +# importc: "mysql_stmt_field_count".} +# proc commit*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_commit".} +# proc rollback*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rollback".} +# proc autocommit*(MySQL: PMySQL, auto_mode: my_bool): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_autocommit".} +# proc more_results*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_more_results".} +# proc next_result*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_next_result".} +proc close*(sock: PMySQL){.stdcall, dynlib: lib, importc: "mysql_close".} + # status return codes +# const +# NO_DATA* = 100 +# DATA_TRUNCATED* = 101 + +# proc reload*(x: PMySQL): cint +# when defined(USE_OLD_FUNCTIONS): +# proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall, +# dynlib: lib, importc: "mysql_connect".} +# proc create_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_create_db".} +# proc drop_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_drop_db".} +# proc net_safe_read*(MySQL: PMySQL): cuint{.cdecl, dynlib: lib, importc: "net_safe_read".} + +# proc IS_PRI_KEY(n: int32): bool = +# result = (n and PRI_KEY_FLAG) != 0 + +# proc IS_NOT_NULL(n: int32): bool = +# result = (n and NOT_NULL_FLAG) != 0 + +# proc IS_BLOB(n: int32): bool = +# result = (n and BLOB_FLAG) != 0 + +# proc IS_NUM_FIELD(f: Pst_mysql_field): bool = +# result = (f.flags and NUM_FLAG) != 0 + +# proc IS_NUM(t: Enum_field_types): bool = +# result = (t <= FIELD_TYPE_INT24) or (t == FIELD_TYPE_YEAR) or +# (t == FIELD_TYPE_NEWDECIMAL) + +# proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool = +# result = (f.ftype <= FIELD_TYPE_INT24) and +# ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or +# (f.ftype == FIELD_TYPE_YEAR) + +# proc reload(x: PMySQL): cint = +# result = refresh(x, REFRESH_GRANT) + +# {.pop.} +# when defined(nimHasStyleChecks): +# {.pop.} diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim.bk b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim.bk new file mode 100644 index 0000000..24a56e2 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/mariadb/mariadb_rdb.nim.bk @@ -0,0 +1,1158 @@ +import ../../../../utils + +{.push, callconv: cdecl.} +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(unix): + when defined(macosx): + const lib = "libmariadb(|.21|.20|.19|.18|.17|.16|.15).dylib" + else: + const osName = getOsName() + when osName == "alpine": + const lib = "libmariadb.so(|.3)" + else: + # const lib = "libmysqlclient.so(|.21|.20|.19|.18|.17|.16|.15)" + const lib = "libmariadbclient.so(|.21|.20|.19|.18|.17|.16|.15)" +when defined(windows): + const lib = "libmariadb.dll" + +type + my_bool* = bool + Pmy_bool* = ptr my_bool + PVIO* = pointer + Pgptr* = ptr gptr + gptr* = cstring + Pmy_socket* = ptr my_socket + my_socket* = cint + PPByte* = pointer + cuint* = cint + +type NetAsyncStatus* = enum + NET_ASYNC_COMPLETE + NET_ASYNC_NOT_READY + NET_ASYNC_ERROR + NET_ASYNC_COMPLETE_NO_MORE_RESULTS + +# ------------ Start of declaration in "mysql_com.h" --------------------- +# +# ** Common definition between mysql server & client +# +# Field/table name length + +const + NAME_LEN* = 64 + HOSTNAME_LENGTH* = 60 + USERNAME_LENGTH* = 16 + SERVER_VERSION_LENGTH* = 60 + SQLSTATE_LENGTH* = 5 + LOCAL_HOST* = "localhost" + LOCAL_HOST_NAMEDPIPE* = '.' + +const + NAMEDPIPE* = "MySQL" + SERVICENAME* = "MySQL" + +type + Enum_server_command* = enum + COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB, + COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO, + COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME, + COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP, + COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE, + COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION, + COM_STMT_FETCH, COM_END +{.deprecated: [Tenum_server_command: Enum_server_command].} + +const + SCRAMBLE_LENGTH* = 20 # Length of random string sent by server on handshake; + # this is also length of obfuscated password, + # received from client + SCRAMBLE_LENGTH_323* = 8 # length of password stored in the db: + # new passwords are preceded with '*' + SCRAMBLED_PASSWORD_CHAR_LENGTH* = SCRAMBLE_LENGTH * 2 + 1 + SCRAMBLED_PASSWORD_CHAR_LENGTH_323* = SCRAMBLE_LENGTH_323 * 2 + NOT_NULL_FLAG* = 1 # Field can't be NULL + PRI_KEY_FLAG* = 2 # Field is part of a primary key + UNIQUE_KEY_FLAG* = 4 # Field is part of a unique key + MULTIPLE_KEY_FLAG* = 8 # Field is part of a key + BLOB_FLAG* = 16 # Field is a blob + UNSIGNED_FLAG* = 32 # Field is unsigned + ZEROFILL_FLAG* = 64 # Field is zerofill + BINARY_FLAG* = 128 # Field is binary + # The following are only sent to new clients + ENUM_FLAG* = 256 # field is an enum + AUTO_INCREMENT_FLAG* = 512 # field is a autoincrement field + TIMESTAMP_FLAG* = 1024 # Field is a timestamp + SET_FLAG* = 2048 # field is a set + NO_DEFAULT_VALUE_FLAG* = 4096 # Field doesn't have default value + NUM_FLAG* = 32768 # Field is num (for clients) + PART_KEY_FLAG* = 16384 # Intern; Part of some key + GROUP_FLAG* = 32768 # Intern: Group field + UNIQUE_FLAG* = 65536 # Intern: Used by sql_yacc + BINCMP_FLAG* = 131072 # Intern: Used by sql_yacc + REFRESH_GRANT* = 1 # Refresh grant tables + REFRESH_LOG* = 2 # Start on new log file + REFRESH_TABLES* = 4 # close all tables + REFRESH_HOSTS* = 8 # Flush host cache + REFRESH_STATUS* = 16 # Flush status variables + REFRESH_THREADS* = 32 # Flush thread cache + REFRESH_SLAVE* = 64 # Reset master info and restart slave thread + REFRESH_MASTER* = 128 # Remove all bin logs in the index and truncate the index + # The following can't be set with mysql_refresh() + REFRESH_READ_LOCK* = 16384 # Lock tables for read + REFRESH_FAST* = 32768 # Intern flag + REFRESH_QUERY_CACHE* = 65536 # RESET (remove all queries) from query cache + REFRESH_QUERY_CACHE_FREE* = 0x00020000 # pack query cache + REFRESH_DES_KEY_FILE* = 0x00040000 + REFRESH_USER_RESOURCES* = 0x00080000 + CLIENT_LONG_PASSWORD* = 1 # new more secure passwords + CLIENT_FOUND_ROWS* = 2 # Found instead of affected rows + CLIENT_LONG_FLAG* = 4 # Get all column flags + CLIENT_CONNECT_WITH_DB* = 8 # One can specify db on connect + CLIENT_NO_SCHEMA* = 16 # Don't allow database.table.column + CLIENT_COMPRESS* = 32 # Can use compression protocol + CLIENT_ODBC* = 64 # Odbc client + CLIENT_LOCAL_FILES* = 128 # Can use LOAD DATA LOCAL + CLIENT_IGNORE_SPACE* = 256 # Ignore spaces before '(' + CLIENT_PROTOCOL_41* = 512 # New 4.1 protocol + CLIENT_INTERACTIVE* = 1024 # This is an interactive client + CLIENT_SSL* = 2048 # Switch to SSL after handshake + CLIENT_IGNORE_SIGPIPE* = 4096 # IGNORE sigpipes + CLIENT_TRANSACTIONS* = 8192 # Client knows about transactions + CLIENT_RESERVED* = 16384 # Old flag for 4.1 protocol + CLIENT_SECURE_CONNECTION* = 32768 # New 4.1 authentication + CLIENT_MULTI_STATEMENTS* = 65536 # Enable/disable multi-stmt support + CLIENT_MULTI_RESULTS* = 131072 # Enable/disable multi-results + CLIENT_REMEMBER_OPTIONS*: int = 1 shl 31 + SERVER_STATUS_IN_TRANS* = 1 # Transaction has started + SERVER_STATUS_AUTOCOMMIT* = 2 # Server in auto_commit mode + SERVER_STATUS_MORE_RESULTS* = 4 # More results on server + SERVER_MORE_RESULTS_EXISTS* = 8 # Multi query - next query exists + SERVER_QUERY_NO_GOOD_INDEX_USED* = 16 + SERVER_QUERY_NO_INDEX_USED* = 32 # The server was able to fulfill the clients request and opened a + # read-only non-scrollable cursor for a query. This flag comes + # in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. + SERVER_STATUS_CURSOR_EXISTS* = 64 # This flag is sent when a read-only cursor is exhausted, in reply to + # COM_STMT_FETCH command. + SERVER_STATUS_LAST_ROW_SENT* = 128 + SERVER_STATUS_DB_DROPPED* = 256 # A database was dropped + SERVER_STATUS_NO_BACKSLASH_ESCAPES* = 512 + ERRMSG_SIZE* = 200 + NET_READ_TIMEOUT* = 30 # Timeout on read + NET_WRITE_TIMEOUT* = 60 # Timeout on write + NET_WAIT_TIMEOUT* = 8 * 60 * 60 # Wait for new query + ONLY_KILL_QUERY* = 1 + +const + MAX_TINYINT_WIDTH* = 3 # Max width for a TINY w.o. sign + MAX_SMALLINT_WIDTH* = 5 # Max width for a SHORT w.o. sign + MAX_MEDIUMINT_WIDTH* = 8 # Max width for a INT24 w.o. sign + MAX_INT_WIDTH* = 10 # Max width for a LONG w.o. sign + MAX_BIGINT_WIDTH* = 20 # Max width for a LONGLONG + MAX_CHAR_WIDTH* = 255 # Max length for a CHAR column + MAX_BLOB_WIDTH* = 8192 # Default width for blob + +type + Pst_net* = ptr St_net + St_net*{.final.} = object + vio*: PVIO + buff*: cstring + buff_end*: cstring + write_pos*: cstring + read_pos*: cstring + fd*: my_socket # For Perl DBI/dbd + max_packet*: int + max_packet_size*: int + pkt_nr*: cuint + compress_pkt_nr*: cuint + write_timeout*: cuint + read_timeout*: cuint + retry_count*: cuint + fcntl*: cint + compress*: my_bool # The following variable is set if we are doing several queries in one + # command ( as in LOAD TABLE ... FROM MASTER ), + # and do not want to confuse the client with OK at the wrong time + remain_in_buf*: int + len*: int + buf_length*: int + where_b*: int + return_status*: ptr cint + reading_or_writing*: char + save_char*: cchar + no_send_ok*: my_bool # For SPs and other things that do multiple stmts + no_send_eof*: my_bool # For SPs' first version read-only cursors + no_send_error*: my_bool # Set if OK packet is already sent, and + # we do not need to send error messages + # Pointer to query object in query cache, do not equal NULL (0) for + # queries in cache that have not stored its results yet + # $endif + last_error*: array[0..(ERRMSG_SIZE) - 1, char] + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + last_errno*: cuint + error*: char + query_cache_query*: gptr + report_error*: my_bool # We should report error (we have unreported error) + return_errno*: my_bool + + NET* = St_net + PNET* = ptr NET +{.deprecated: [Tst_net: St_net, TNET: NET].} + +const + packet_error* = - 1 + +type + Enum_field_types* = enum # For backward compatibility + TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, + TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME, + TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT, + TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248, + TYPE_TINY_BLOB = 249, TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251, + TYPE_BLOB = 252, TYPE_VAR_STRING = 253, TYPE_STRING = 254, + TYPE_GEOMETRY = 255 +{.deprecated: [Tenum_field_types: Enum_field_types].} + +const + CLIENT_MULTI_QUERIES* = CLIENT_MULTI_STATEMENTS + FIELD_TYPE_DECIMAL* = TYPE_DECIMAL + FIELD_TYPE_NEWDECIMAL* = TYPE_NEWDECIMAL + FIELD_TYPE_TINY* = TYPE_TINY + FIELD_TYPE_SHORT* = TYPE_SHORT + FIELD_TYPE_LONG* = TYPE_LONG + FIELD_TYPE_FLOAT* = TYPE_FLOAT + FIELD_TYPE_DOUBLE* = TYPE_DOUBLE + FIELD_TYPE_NULL* = TYPE_NULL + FIELD_TYPE_TIMESTAMP* = TYPE_TIMESTAMP + FIELD_TYPE_LONGLONG* = TYPE_LONGLONG + FIELD_TYPE_INT24* = TYPE_INT24 + FIELD_TYPE_DATE* = TYPE_DATE + FIELD_TYPE_TIME* = TYPE_TIME + FIELD_TYPE_DATETIME* = TYPE_DATETIME + FIELD_TYPE_YEAR* = TYPE_YEAR + FIELD_TYPE_NEWDATE* = TYPE_NEWDATE + FIELD_TYPE_ENUM* = TYPE_ENUM + FIELD_TYPE_SET* = TYPE_SET + FIELD_TYPE_TINY_BLOB* = TYPE_TINY_BLOB + FIELD_TYPE_MEDIUM_BLOB* = TYPE_MEDIUM_BLOB + FIELD_TYPE_LONG_BLOB* = TYPE_LONG_BLOB + FIELD_TYPE_BLOB* = TYPE_BLOB + FIELD_TYPE_VAR_STRING* = TYPE_VAR_STRING + FIELD_TYPE_STRING* = TYPE_STRING + FIELD_TYPE_CHAR* = TYPE_TINY + FIELD_TYPE_INTERVAL* = TYPE_ENUM + FIELD_TYPE_GEOMETRY* = TYPE_GEOMETRY + FIELD_TYPE_BIT* = TYPE_BIT # Shutdown/kill enums and constants + # Bits for THD::killable. + SHUTDOWN_KILLABLE_CONNECT* = chr(1 shl 0) + SHUTDOWN_KILLABLE_TRANS* = chr(1 shl 1) + SHUTDOWN_KILLABLE_LOCK_TABLE* = chr(1 shl 2) + SHUTDOWN_KILLABLE_UPDATE* = chr(1 shl 3) + +type + Enum_shutdown_level* = enum + SHUTDOWN_DEFAULT = 0, SHUTDOWN_WAIT_CONNECTIONS = 1, + SHUTDOWN_WAIT_TRANSACTIONS = 2, SHUTDOWN_WAIT_UPDATES = 8, + SHUTDOWN_WAIT_ALL_BUFFERS = 16, SHUTDOWN_WAIT_CRITICAL_BUFFERS = 17, + KILL_QUERY = 254, KILL_CONNECTION = 255 + Enum_cursor_type* = enum # options for mysql_set_option + CURSOR_TYPE_NO_CURSOR = 0, CURSOR_TYPE_READ_ONLY = 1, + CURSOR_TYPE_FOR_UPDATE = 2, CURSOR_TYPE_SCROLLABLE = 4 + Enum_mysql_set_option* = enum + OPTION_MULTI_STATEMENTS_ON, OPTION_MULTI_STATEMENTS_OFF +{.deprecated: [Tenum_shutdown_level: Enum_shutdown_level, + Tenum_cursor_type: Enum_cursor_type, + Tenum_mysql_set_option: Enum_mysql_set_option].} + +proc my_net_init*(net: PNET, vio: PVIO): my_bool{.cdecl, dynlib: lib, + importc: "my_net_init".} +proc my_net_local_init*(net: PNET){.cdecl, dynlib: lib, + importc: "my_net_local_init".} +proc net_end*(net: PNET){.cdecl, dynlib: lib, importc: "net_end".} +proc net_clear*(net: PNET){.cdecl, dynlib: lib, importc: "net_clear".} +proc net_realloc*(net: PNET, len: int): my_bool{.cdecl, dynlib: lib, + importc: "net_realloc".} +proc net_flush*(net: PNET): my_bool{.cdecl, dynlib: lib, importc: "net_flush".} +proc my_net_write*(net: PNET, packet: cstring, length: int): my_bool{.cdecl, + dynlib: lib, importc: "my_net_write".} +proc net_write_command*(net: PNET, command: char, header: cstring, + head_len: int, packet: cstring, length: int): my_bool{. + cdecl, dynlib: lib, importc: "net_write_command".} +proc net_real_write*(net: PNET, packet: cstring, length: int): cint{.cdecl, + dynlib: lib, importc: "net_real_write".} +proc my_net_read*(net: PNET): int{.cdecl, dynlib: lib, importc: "my_net_read".} + # The following function is not meant for normal usage + # Currently it's used internally by manager.c +type + Psockaddr* = ptr Sockaddr + Sockaddr*{.final.} = object # undefined structure +{.deprecated: [Tsockaddr: Sockaddr].} + +proc my_connect*(s: my_socket, name: Psockaddr, namelen: cuint, timeout: cuint): cint{. + cdecl, dynlib: lib, importc: "my_connect".} +type + Prand_struct* = ptr Rand_struct + Rand_struct*{.final.} = object # The following is for user defined functions + seed1*: int + seed2*: int + max_value*: int + max_value_dbl*: cdouble + + Item_result* = enum + STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT + PItem_result* = ptr Item_result + Pst_udf_args* = ptr St_udf_args + St_udf_args*{.final.} = object + arg_count*: cuint # Number of arguments + arg_type*: PItem_result # Pointer to item_results + args*: cstringArray # Pointer to item_results + lengths*: ptr int # Length of string arguments + maybe_null*: cstring # Length of string arguments + attributes*: cstringArray # Pointer to attribute name + attribute_lengths*: ptr int # Length of attribute arguments + + UDF_ARGS* = St_udf_args + PUDF_ARGS* = ptr UDF_ARGS # This holds information about the result + Pst_udf_init* = ptr St_udf_init + St_udf_init*{.final.} = object + maybe_null*: my_bool # 1 if function can return NULL + decimals*: cuint # for real functions + max_length*: int # For string functions + theptr*: cstring # free pointer for function data + const_item*: my_bool # free pointer for function data + + UDF_INIT* = St_udf_init + PUDF_INIT* = ptr UDF_INIT # Constants when using compression +{.deprecated: [Trand_stuct: Rand_struct, TItem_result: Item_result, + Tst_udf_args: St_udf_args, TUDF_ARGS: UDF_ARGS, + Tst_udf_init: St_udf_init, TUDF_INIT: UDF_INIT].} + +const + NET_HEADER_SIZE* = 4 # standard header size + COMP_HEADER_SIZE* = 3 # compression header extra size + # Prototypes to password functions + # These functions are used for authentication by client and server and + # implemented in sql/password.c + +proc randominit*(para1: Prand_struct, seed1: int, seed2: int){.cdecl, + dynlib: lib, importc: "randominit".} +proc my_rnd*(para1: Prand_struct): cdouble{.cdecl, dynlib: lib, + importc: "my_rnd".} +proc create_random_string*(fto: cstring, len: cuint, rand_st: Prand_struct){. + cdecl, dynlib: lib, importc: "create_random_string".} +proc hash_password*(fto: int, password: cstring, password_len: cuint){.cdecl, + dynlib: lib, importc: "hash_password".} +proc make_scrambled_password_323*(fto: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "make_scrambled_password_323".} +proc scramble_323*(fto: cstring, message: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "scramble_323".} +proc check_scramble_323*(para1: cstring, message: cstring, salt: int): my_bool{. + cdecl, dynlib: lib, importc: "check_scramble_323".} +proc get_salt_from_password_323*(res: ptr int, password: cstring){.cdecl, + dynlib: lib, importc: "get_salt_from_password_323".} +proc make_password_from_salt_323*(fto: cstring, salt: ptr int){.cdecl, + dynlib: lib, importc: "make_password_from_salt_323".} +proc octet2hex*(fto: cstring, str: cstring, length: cuint): cstring{.cdecl, + dynlib: lib, importc: "octet2hex".} +proc make_scrambled_password*(fto: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "make_scrambled_password".} +proc scramble*(fto: cstring, message: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "scramble".} +proc check_scramble*(reply: cstring, message: cstring, hash_stage2: pointer): my_bool{. + cdecl, dynlib: lib, importc: "check_scramble".} +proc get_salt_from_password*(res: pointer, password: cstring){.cdecl, + dynlib: lib, importc: "get_salt_from_password".} +proc make_password_from_salt*(fto: cstring, hash_stage2: pointer){.cdecl, + dynlib: lib, importc: "make_password_from_salt".} + # end of password.c +proc get_tty_password*(opt_message: cstring): cstring{.cdecl, dynlib: lib, + importc: "get_tty_password".} +proc errno_to_sqlstate*(errno: cuint): cstring{.cdecl, dynlib: lib, + importc: "mysql_errno_to_sqlstate".} + # Some other useful functions +proc modify_defaults_file*(file_location: cstring, option: cstring, + option_value: cstring, section_name: cstring, + remove_option: cint): cint{.cdecl, dynlib: lib, + importc: "load_defaults".} +proc load_defaults*(conf_file: cstring, groups: cstringArray, argc: ptr cint, + argv: ptr cstringArray): cint{.cdecl, dynlib: lib, + importc: "load_defaults".} +proc my_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_init".} +proc my_thread_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_thread_init".} +proc my_thread_end*(){.cdecl, dynlib: lib, importc: "my_thread_end".} +const + NULL_LENGTH*: int = int(not (0)) # For net_store_length + +const + STMT_HEADER* = 4 + LONG_DATA_HEADER* = 6 # ------------ Stop of declaration in "mysql_com.h" ----------------------- + # $include "mysql_time.h" + # $include "mysql_version.h" + # $include "typelib.h" + # $include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */ + # var + # mysql_port : cuint;cvar;external; + # mysql_unix_port : Pchar;cvar;external; + +const + CLIENT_NET_READ_TIMEOUT* = 365 * 24 * 3600 # Timeout on read + CLIENT_NET_WRITE_TIMEOUT* = 365 * 24 * 3600 # Timeout on write + +type + Pst_mysql_field* = ptr St_mysql_field + St_mysql_field*{.final.} = object + name*: cstring # Name of column + org_name*: cstring # Original column name, if an alias + table*: cstring # Table of column if column was a field + org_table*: cstring # Org table name, if table was an alias + db*: cstring # Database for table + catalog*: cstring # Catalog for table + def*: cstring # Default value (set by mysql_list_fields) + len*: int # Width of column (create length) + max_length*: int # Max width for selected set + name_length*: cuint + org_name_length*: cuint + table_length*: cuint + org_table_length*: cuint + db_length*: cuint + catalog_length*: cuint + def_length*: cuint + flags*: cuint # Div flags + decimals*: cuint # Number of decimals in field + charsetnr*: cuint # Character set + ftype*: Enum_field_types # Type of field. See mysql_com.h for types + extension*: pointer + + FIELD* = St_mysql_field + PFIELD* = ptr FIELD + PROW* = ptr ROW # return data as array of strings + ROW* = cstringArray + PFIELD_OFFSET* = ptr FIELD_OFFSET # offset to current field + FIELD_OFFSET* = cuint +{.deprecated: [Tst_mysql_field: St_mysql_field, TFIELD: FIELD, TROW: ROW, + TFIELD_OFFSET: FIELD_OFFSET].} + +proc IS_PRI_KEY*(n: int32): bool +proc IS_NOT_NULL*(n: int32): bool +proc IS_BLOB*(n: int32): bool +proc IS_NUM*(t: Enum_field_types): bool +proc INTERNAL_NUM_FIELD*(f: Pst_mysql_field): bool +proc IS_NUM_FIELD*(f: Pst_mysql_field): bool +type + my_ulonglong* = int64 + Pmy_ulonglong* = ptr my_ulonglong + +const + COUNT_ERROR* = not (my_ulonglong(0)) + +type + Pst_mysql_rows* = ptr St_mysql_rows + St_mysql_rows*{.final.} = object + next*: Pst_mysql_rows # list of rows + data*: ROW + len*: int + + ROWS* = St_mysql_rows + PROWS* = ptr ROWS + PROW_OFFSET* = ptr ROW_OFFSET # offset to current row + ROW_OFFSET* = ROWS +{.deprecated: [Tst_mysql_rows: St_mysql_rows, TROWS: ROWS, + TROW_OFFSET: ROW_OFFSET].} + +const + ALLOC_MAX_BLOCK_TO_DROP* = 4096 + ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP* = 10 # struct for once_alloc (block) + +type + Pst_used_mem* = ptr St_used_mem + St_used_mem*{.final.} = object + next*: Pst_used_mem # Next block in use + left*: cuint # memory left in block + size*: cuint # size of block + + USED_MEM* = St_used_mem + PUSED_MEM* = ptr USED_MEM + Pst_mem_root* = ptr St_mem_root + St_mem_root*{.final.} = object + free*: PUSED_MEM # blocks with free memory in it + used*: PUSED_MEM # blocks almost without free memory + pre_alloc*: PUSED_MEM # preallocated block + min_malloc*: cuint # if block have less memory it will be put in 'used' list + block_size*: cuint # initial block size + block_num*: cuint # allocated blocks counter + # first free block in queue test counter (if it exceed + # MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list) + first_block_usage*: cuint + error_handler*: proc (){.cdecl.} + + MEM_ROOT* = St_mem_root + PMEM_ROOT* = ptr MEM_ROOT # ------------ Stop of declaration in "my_alloc.h" ---------------------- +{.deprecated: [Tst_used_mem: St_used_mem, TUSED_MEM: USED_MEM, + Tst_mem_root: St_mem_root, TMEM_ROOT: MEM_ROOT].} + +type + Pst_mysql_data* = ptr St_mysql_data + St_mysql_data*{.final.} = object + rows*: my_ulonglong + fields*: cuint + data*: PROWS + alloc*: MEM_ROOT + prev_ptr*: ptr PROWS + + DATA* = St_mysql_data + PDATA* = ptr DATA + Option* = enum + OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND, + READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME, + OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT, + OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION, + OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP, + SECURE_AUTH, REPORT_DATA_TRUNCATION, OPT_RECONNECT, + MYSQL_OPT_NONBLOCK +{.deprecated: [Tst_mysql_data: St_mysql_data, TDATA: DATA, Toption: Option].} + +const + MAX_MYSQL_MANAGER_ERR* = 256 + MAX_MYSQL_MANAGER_MSG* = 256 + MANAGER_OK* = 200 + MANAGER_INFO* = 250 + MANAGER_ACCESS* = 401 + MANAGER_CLIENT_ERR* = 450 + MANAGER_INTERNAL_ERR* = 500 + +type + St_dynamic_array*{.final.} = object + buffer*: cstring + elements*: cuint + max_element*: cuint + alloc_increment*: cuint + size_of_element*: cuint + + DYNAMIC_ARRAY* = St_dynamic_array + Pst_dynamic_array* = ptr St_dynamic_array + Pst_mysql_options* = ptr St_mysql_options + St_mysql_options*{.final.} = object + connect_timeout*: cuint + read_timeout*: cuint + write_timeout*: cuint + port*: cuint + protocol*: cuint + client_flag*: int + host*: cstring + user*: cstring + password*: cstring + unix_socket*: cstring + db*: cstring + init_commands*: Pst_dynamic_array + my_cnf_file*: cstring + my_cnf_group*: cstring + charset_dir*: cstring + charset_name*: cstring + ssl_key*: cstring # PEM key file + ssl_cert*: cstring # PEM cert file + ssl_ca*: cstring # PEM CA file + ssl_capath*: cstring # PEM directory of CA-s? + ssl_cipher*: cstring # cipher to use + shared_memory_base_name*: cstring + max_allowed_packet*: int + use_ssl*: my_bool # if to use SSL or not + compress*: my_bool + named_pipe*: my_bool # On connect, find out the replication role of the server, and + # establish connections to all the peers + rpl_probe*: my_bool # Each call to mysql_real_query() will parse it to tell if it is a read + # or a write, and direct it to the slave or the master + rpl_parse*: my_bool # If set, never read from a master, only from slave, when doing + # a read that is replication-aware + no_master_reads*: my_bool + separate_thread*: my_bool + methods_to_use*: Option + client_ip*: cstring + secure_auth*: my_bool # Refuse client connecting to server if it uses old (pre-4.1.1) protocol + report_data_truncation*: my_bool # 0 - never report, 1 - always report (default) + # function pointers for local infile support + local_infile_init*: proc (para1: var pointer, para2: cstring, para3: pointer): cint{. + cdecl.} + local_infile_read*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_end*: proc (para1: pointer) + local_infile_error*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_userdata*: pointer + + Status* = enum + STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT + Protocol_type* = enum # There are three types of queries - the ones that have to go to + # the master, the ones that go to a slave, and the administrative + # type which must happen on the pivot connection + PROTOCOL_DEFAULT, PROTOCOL_TCP, PROTOCOL_SOCKET, PROTOCOL_PIPE, + PROTOCOL_MEMORY + Rpl_type* = enum + RPL_MASTER, RPL_SLAVE, RPL_ADMIN + Charset_info_st*{.final.} = object + number*: cuint + primary_number*: cuint + binary_number*: cuint + state*: cuint + csname*: cstring + name*: cstring + comment*: cstring + tailoring*: cstring + ftype*: cstring + to_lower*: cstring + to_upper*: cstring + sort_order*: cstring + contractions*: ptr int16 + sort_order_big*: ptr ptr int16 + tab_to_uni*: ptr int16 + tab_from_uni*: pointer # was ^MY_UNI_IDX + state_map*: cstring + ident_map*: cstring + strxfrm_multiply*: cuint + mbminlen*: cuint + mbmaxlen*: cuint + min_sort_char*: int16 + max_sort_char*: int16 + escape_with_backslash_is_dangerous*: my_bool + cset*: pointer # was ^MY_CHARSET_HANDLER + coll*: pointer # was ^MY_COLLATION_HANDLER; + + CHARSET_INFO* = Charset_info_st + Pcharset_info_st* = ptr Charset_info_st + Pcharacter_set* = ptr Character_set + Character_set*{.final.} = object + number*: cuint + state*: cuint + csname*: cstring + name*: cstring + comment*: cstring + dir*: cstring + mbminlen*: cuint + mbmaxlen*: cuint + + MY_CHARSET_INFO* = Character_set + PMY_CHARSET_INFO* = ptr MY_CHARSET_INFO + Pst_mysql_methods* = ptr St_mysql_methods + Pst_mysql* = ptr St_mysql + St_mysql*{.final.} = object + net*: NET # Communication parameters + connector_fd*: gptr # ConnectorFd for SSL + host*: cstring + user*: cstring + passwd*: cstring + unix_socket*: cstring + server_version*: cstring + host_info*: cstring + info*: cstring + db*: cstring + charset*: Pcharset_info_st + fields*: PFIELD + field_alloc*: MEM_ROOT + affected_rows*: my_ulonglong + insert_id*: my_ulonglong # id if insert on table with NEXTNR + extra_info*: my_ulonglong # Used by mysqlshow, not used by mysql 5.0 and up + thread_id*: int # Id for connection in server + packet_length*: int + port*: cuint + client_flag*: int + server_capabilities*: int + protocol_version*: cuint + field_count*: cuint + server_status*: cuint + server_language*: cuint + warning_count*: cuint + options*: St_mysql_options + status*: Status + free_me*: my_bool # If free in mysql_close + reconnect*: my_bool # set to 1 if automatic reconnect + scramble*: array[0..(SCRAMBLE_LENGTH + 1) - 1, char] # session-wide random string + # Set if this is the original connection, not a master or a slave we have + # added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave() + rpl_pivot*: my_bool # Pointers to the master, and the next slave connections, points to + # itself if lone connection. + master*: Pst_mysql + next_slave*: Pst_mysql + last_used_slave*: Pst_mysql # needed for round-robin slave pick + last_used_con*: Pst_mysql # needed for send/read/store/use result to work correctly with replication + stmts*: pointer # was PList, list of all statements + methods*: Pst_mysql_methods + thd*: pointer # Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag + # from mysql_stmt_close if close had to cancel result set of this object. + unbuffered_fetch_owner*: Pmy_bool + + MySQL* = St_mysql + PMySQL* = ptr MySQL + Pst_mysql_res* = ptr St_mysql_res + St_mysql_res*{.final.} = object + row_count*: my_ulonglong + fields*: PFIELD + data*: PDATA + data_cursor*: PROWS + lengths*: ptr int # column lengths of current row + handle*: PMySQL # for unbuffered reads + field_alloc*: MEM_ROOT + field_count*: cuint + current_field*: cuint + row*: ROW # If unbuffered read + current_row*: ROW # buffer to current row + eof*: my_bool # Used by mysql_fetch_row + unbuffered_fetch_cancelled*: my_bool # mysql_stmt_close() had to cancel this result + methods*: Pst_mysql_methods + + RES* = St_mysql_res + PRES* = ptr RES + Pst_mysql_stmt* = ptr St_mysql_stmt + PSTMT* = ptr STMT + St_mysql_methods*{.final.} = object + read_query_result*: proc (MySQL: PMySQL): my_bool{.cdecl.} + advanced_command*: proc (MySQL: PMySQL, command: Enum_server_command, header: cstring, + header_length: int, arg: cstring, arg_length: int, + skip_check: my_bool): my_bool + read_rows*: proc (MySQL: PMySQL, fields: PFIELD, fields_count: cuint): PDATA + use_result*: proc (MySQL: PMySQL): PRES + fetch_lengths*: proc (fto: ptr int, column: ROW, field_count: cuint) + flush_use_result*: proc (MySQL: PMySQL) + list_fields*: proc (MySQL: PMySQL): PFIELD + read_prepare_result*: proc (MySQL: PMySQL, stmt: PSTMT): my_bool + stmt_execute*: proc (stmt: PSTMT): cint + read_binary_rows*: proc (stmt: PSTMT): cint + unbuffered_fetch*: proc (MySQL: PMySQL, row: cstringArray): cint + free_embedded_thd*: proc (MySQL: PMySQL) + read_statistics*: proc (MySQL: PMySQL): cstring + next_result*: proc (MySQL: PMySQL): my_bool + read_change_user_result*: proc (MySQL: PMySQL, buff: cstring, passwd: cstring): cint + read_rowsfrom_cursor*: proc (stmt: PSTMT): cint + + METHODS* = St_mysql_methods + PMETHODS* = ptr METHODS + Pst_mysql_manager* = ptr St_mysql_manager + St_mysql_manager*{.final.} = object + net*: NET + host*: cstring + user*: cstring + passwd*: cstring + port*: cuint + free_me*: my_bool + eof*: my_bool + cmd_status*: cint + last_errno*: cint + net_buf*: cstring + net_buf_pos*: cstring + net_data_end*: cstring + net_buf_size*: cint + last_error*: array[0..(MAX_MYSQL_MANAGER_ERR) - 1, char] + + MANAGER* = St_mysql_manager + PMANAGER* = ptr MANAGER + Pst_mysql_parameters* = ptr St_mysql_parameters + St_mysql_parameters*{.final.} = object + p_max_allowed_packet*: ptr int + p_net_buffer_length*: ptr int + + PARAMETERS* = St_mysql_parameters + PPARAMETERS* = ptr PARAMETERS + Enum_mysql_stmt_state* = enum + STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE + Pst_mysql_bind* = ptr St_mysql_bind + # St_mysql_bind*{.final.} = object + # len*: int # output length pointer + # is_null*: Pmy_bool # Pointer to null indicator + # buffer*: pointer # buffer to get/put data + # error*: Pmy_bool # set this if you want to track data truncations happened during fetch + # buffer_type*: Enum_field_types # buffer type + # buffer_length*: int # buffer length, must be set for str/binary + # # Following are for internal use. Set by mysql_stmt_bind_param + # row_ptr*: ptr byte # for the current data position + # offset*: int # offset position for char/binary fetch + # length_value*: int # Used if length is 0 + # param_number*: cuint # For null count and error messages + # pack_length*: cuint # Internal length for packed data + # error_value*: my_bool # used if error is 0 + # is_unsigned*: my_bool # set if integer type is unsigned + # long_data_used*: my_bool # If used with mysql_send_long_data + # is_null_value*: my_bool # Used if is_null is 0 + # store_param_func*: proc (net: PNET, param: Pst_mysql_bind){.cdecl.} + # fetch_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) + # skip_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) + + U* {.final, union, pure.} = object # union for row_ptr and indicator + row_ptr*: ptr byte # for the current data position + indicator*: cstring + + St_mysql_bind*{.final.} = object + length*: ptr culong # output length pointer + is_null*: Pmy_bool # Pointer to null indicator + buffer*: pointer # buffer to get/put data + error*: Pmy_bool # set this if you want to track data truncations happened during fetch + u*: U + store_param_func*: proc (net: PNET, param: ptr St_mysql_bind){.cdecl.} + fetch_result*: proc (param: ptr St_mysql_bind, field: PFIELD, row: PPByte){.cdecl.} + skip_result*: proc (param: ptr St_mysql_bind, field: PFIELD, row: PPByte){.cdecl.} + buffer_length*: culong # buffer length, must be set when fetching str/binary + offset*: culong # offset position for char/binary fetch + length_value*: culong # Used if length is 0 + flags*: cuint # special flags, e.g. for dummy bind + pack_length*: cuint # Internal length for packed data + buffer_type*: Enum_field_types # buffer type + error_value*: my_bool # used if error is 0 + is_unsigned*: my_bool # set if integer type is unsigned + long_data_used*: my_bool # If used with mysql_send_long_data + is_null_value*: my_bool # Used if is_null is 0 + extension*: pointer + + BIND* = St_mysql_bind + PBIND* = ptr BIND # statement handler + St_mysql_stmt*{.final.} = object + mem_root*: MEM_ROOT # root allocations + mysql*: PMySQL # connection handle + params*: PBIND # input parameters + `bind`*: PBIND # input parameters + fields*: PFIELD # result set metadata + result*: DATA # cached result set + data_cursor*: PROWS # current row in cached result + affected_rows*: my_ulonglong # copy of mysql->affected_rows after statement execution + insert_id*: my_ulonglong + read_row_func*: proc (stmt: Pst_mysql_stmt, row: PPByte): cint{.cdecl.} + stmt_id*: int # Id for prepared statement + flags*: int # i.e. type of cursor to open + prefetch_rows*: int # number of rows per one COM_FETCH + server_status*: cuint # Copied from mysql->server_status after execute/fetch to know + # server-side cursor status for this statement. + last_errno*: cuint # error code + param_count*: cuint # input parameter count + field_count*: cuint # number of columns in result set + state*: Enum_mysql_stmt_state # statement state + last_error*: array[0..(ERRMSG_SIZE) - 1, char] # error message + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + send_types_to_server*: my_bool # Types of input parameters should be sent to server + bind_param_done*: my_bool # input buffers were supplied + bind_result_done*: char # output buffers were supplied + unbuffered_fetch_cancelled*: my_bool + update_max_length*: my_bool + + STMT* = St_mysql_stmt + + Enum_stmt_attr_type* = enum + STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS +{.deprecated: [Tst_dynamic_array: St_dynamic_array, Tst_mysql_options: St_mysql_options, + TDYNAMIC_ARRAY: DYNAMIC_ARRAY, Tprotocol_type: Protocol_type, + Trpl_type: Rpl_type, Tcharset_info_st: Charset_info_st, + TCHARSET_INFO: CHARSET_INFO, Tcharacter_set: Character_set, + TMY_CHARSET_INFO: MY_CHARSET_INFO, Tst_mysql: St_mysql, + Tst_mysql_methods: St_mysql_methods, TMySql: MySql, + Tst_mysql_res: St_mysql_res, TMETHODS: METHODS, TRES: RES, + Tst_mysql_manager: St_mysql_manager, TMANAGER: MANAGER, + Tst_mysql_parameters: St_mysql_parameters, TPARAMETERS: PARAMETERS, + Tenum_mysql_stmt_state: Enum_mysql_stmt_state, + Tst_mysql_bind: St_mysql_bind, TBIND: BIND, Tst_mysql_stmt: St_mysql_stmt, + TSTMT: STMT, Tenum_stmt_attr_type: Enum_stmt_attr_type, + Tstatus: Status].} + +proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. + cdecl, dynlib: lib, importc: "mysql_server_init".} +proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} + # mysql_server_init/end need to be called when using libmysqld or + # libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so + # you don't need to call it explicitly; but you need to call + # mysql_server_end() to free memory). The names are a bit misleading + # (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general + # names which suit well whether you're using libmysqld or libmysqlclient. We + # intend to promote these aliases over the mysql_server* ones. +proc library_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. + cdecl, dynlib: lib, importc: "mysql_server_init".} +proc library_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} +proc get_parameters*(): PPARAMETERS{.stdcall, dynlib: lib, + importc: "mysql_get_parameters".} + # Set up and bring down a thread; these function should be called + # for each thread in an application which opens at least one MySQL + # connection. All uses of the connection(s) should be between these + # function calls. +proc thread_init*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_thread_init".} +proc thread_end*(){.stdcall, dynlib: lib, importc: "mysql_thread_end".} + # Functions to get information from the MYSQL and MYSQL_RES structures + # Should definitely be used if one uses shared libraries. +proc num_rows*(res: PRES): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_num_rows".} +proc num_fields*(res: PRES): cuint{.stdcall, dynlib: lib, + importc: "mysql_num_fields".} +proc eof*(res: PRES): my_bool{.stdcall, dynlib: lib, importc: "mysql_eof".} +proc fetch_field_direct*(res: PRES, fieldnr: cuint): PFIELD{.stdcall, + dynlib: lib, importc: "mysql_fetch_field_direct".} +proc fetch_fields*(res: PRES): PFIELD{.stdcall, dynlib: lib, + importc: "mysql_fetch_fields".} +proc row_tell*(res: PRES): ROW_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_row_tell".} +proc field_tell*(res: PRES): FIELD_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_field_tell".} +proc field_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_field_count".} +proc affected_rows*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_affected_rows".} +proc insert_id*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_insert_id".} +proc errno*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, importc: "mysql_errno".} +proc error*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_error".} +proc sqlstate*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_sqlstate".} +proc warning_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_warning_count".} +proc info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_info".} +proc thread_id*(MySQL: PMySQL): int{.stdcall, dynlib: lib, importc: "mysql_thread_id".} +proc character_set_name*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_character_set_name".} +proc set_character_set*(MySQL: PMySQL, csname: cstring): int32{.stdcall, dynlib: lib, + importc: "mysql_set_character_set".} +proc init*(MySQL: PMySQL): PMySQL{.stdcall, dynlib: lib, importc: "mysql_init".} +# proc init*(MySQL: var PMySQL){.stdcall, dynlib: lib, importc: "mysql_init".} +proc ssl_set*(MySQL: PMySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring, + cipher: cstring): my_bool{.stdcall, dynlib: lib, + importc: "mysql_ssl_set".} +proc change_user*(MySQL: PMySQL, user: cstring, passwd: cstring, db: cstring): my_bool{. + stdcall, dynlib: lib, importc: "mysql_change_user".} +proc real_connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: int): PMySQL{.stdcall, dynlib: lib, + importc: "mysql_real_connect".} +proc real_connect_start*(ret: ptr PMySQL, mysql: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: culong): cint{.stdcall, dynlib: lib, + importc: "mysql_real_connect_start".} +proc fetch_row_nonblocking*(MYSQL_RES: PRES, MYSQL_ROW: PROW): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_fetch_row_nonblocking".} +proc free_result_nonblocking*(MYSQL_RES: PRES): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_free_result_nonblocking".} +proc fetch_next_result_nonblocking*(MySQL: PMySQL): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_next_result_nonblocking".} +proc real_query_nonblocking*(MySQL: PMySQL, q: cstring, length: int): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_real_query_nonblocking".} +proc store_result_nonblocking*(MySQL: PMySQL, MYSQL_RES: ptr PRES): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_store_result_nonblocking".} +proc select_db*(MySQL: PMySQL, db: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_select_db".} +proc query*(MySQL: PMySQL, q: cstring): cint{.stdcall, dynlib: lib, importc: "mysql_query".} +proc send_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_send_query".} +proc real_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_real_query".} +proc store_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, + importc: "mysql_store_result".} +proc use_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, importc: "mysql_use_result".} + # perform query on master +proc master_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_master_query".} +proc master_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, + dynlib: lib, importc: "mysql_master_send_query".} + # perform query on slave +proc slave_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_slave_query".} +proc slave_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, + dynlib: lib, importc: "mysql_slave_send_query".} +proc get_character_set_info*(MySQL: PMySQL, charset: PMY_CHARSET_INFO){.stdcall, + dynlib: lib, importc: "mysql_get_character_set_info".} + # local infile support +const + LOCAL_INFILE_ERROR_LEN* = 512 + +# procedure mysql_set_local_infile_handler(mysql:PMYSQL; local_infile_init:function (para1:Ppointer; para2:Pchar; para3:pointer):longint; local_infile_read:function (para1:pointer; para2:Pchar; para3:dword):longint; local_infile_end:procedure (_pa +# para6:pointer);cdecl;external mysqllib name 'mysql_set_local_infile_handler'; + +proc set_local_infile_default*(MySQL: PMySQL){.cdecl, dynlib: lib, + importc: "mysql_set_local_infile_default".} + # enable/disable parsing of all queries to decide if they go on master or + # slave +proc enable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_enable_rpl_parse".} +proc disable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_disable_rpl_parse".} + # get the value of the parse flag +proc rpl_parse_enabled*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_rpl_parse_enabled".} + # enable/disable reads from master +proc enable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_enable_reads_from_master".} +proc disable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, importc: "mysql_disable_reads_from_master".} + # get the value of the master read flag +proc reads_from_master_enabled*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_reads_from_master_enabled".} +proc rpl_query_type*(q: cstring, length: cint): Rpl_type{.stdcall, dynlib: lib, + importc: "mysql_rpl_query_type".} + # discover the master and its slaves +proc rpl_probe*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rpl_probe".} + # set the master, close/free the old one, if it is not a pivot +proc set_master*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. + stdcall, dynlib: lib, importc: "mysql_set_master".} +proc add_slave*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. + stdcall, dynlib: lib, importc: "mysql_add_slave".} +proc shutdown*(MySQL: PMySQL, shutdown_level: Enum_shutdown_level): cint{.stdcall, + dynlib: lib, importc: "mysql_shutdown".} +proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_dump_debug_info".} +proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib, + importc: "mysql_refresh".} +proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".} +proc set_server_option*(MySQL: PMySQL, option: Enum_mysql_set_option): cint{.stdcall, + dynlib: lib, importc: "mysql_set_server_option".} +proc ping*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_ping".} +proc stat*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_stat".} +proc get_server_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_server_info".} +proc get_client_info*(): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_client_info".} +proc get_client_version*(): int{.stdcall, dynlib: lib, + importc: "mysql_get_client_version".} +proc get_host_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_host_info".} +proc get_server_version*(MySQL: PMySQL): int{.stdcall, dynlib: lib, + importc: "mysql_get_server_version".} +proc get_proto_info*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_get_proto_info".} +proc list_dbs*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_dbs".} +proc list_tables*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_tables".} +proc list_processes*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_processes".} +proc options*(MySQL: PMySQL, option: Option, arg: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_options".} +proc free_result*(result: PRES){.stdcall, dynlib: lib, + importc: "mysql_free_result".} +proc data_seek*(result: PRES, offset: my_ulonglong){.stdcall, dynlib: lib, + importc: "mysql_data_seek".} +proc row_seek*(result: PRES, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_row_seek".} +proc field_seek*(result: PRES, offset: FIELD_OFFSET): FIELD_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_field_seek".} +proc fetch_row*(result: PRES): ROW{.stdcall, dynlib: lib, + importc: "mysql_fetch_row".} +proc fetch_lengths*(result: PRES): ptr int{.stdcall, dynlib: lib, + importc: "mysql_fetch_lengths".} +proc fetch_field*(result: PRES): PFIELD{.stdcall, dynlib: lib, + importc: "mysql_fetch_field".} +proc list_fields*(MySQL: PMySQL, table: cstring, wild: cstring): PRES{.stdcall, + dynlib: lib, importc: "mysql_list_fields".} +proc escape_string*(fto: cstring, `from`: cstring, from_length: int): int{. + stdcall, dynlib: lib, importc: "mysql_escape_string".} +proc hex_string*(fto: cstring, `from`: cstring, from_length: int): int{.stdcall, + dynlib: lib, importc: "mysql_hex_string".} +proc real_escape_string*(MySQL: PMySQL, fto: cstring, `from`: cstring, len: int): int{. + stdcall, dynlib: lib, importc: "mysql_real_escape_string".} +proc debug*(debug: cstring){.stdcall, dynlib: lib, importc: "mysql_debug".} + # function mysql_odbc_escape_string(mysql:PMYSQL; fto:Pchar; to_length:dword; from:Pchar; from_length:dword; + # param:pointer; extend_buffer:function (para1:pointer; to:Pchar; length:Pdword):Pchar):Pchar;stdcall;external mysqllib name 'mysql_odbc_escape_string'; +proc myodbc_remove_escape*(MySQL: PMySQL, name: cstring){.stdcall, dynlib: lib, + importc: "myodbc_remove_escape".} +proc thread_safe*(): cuint{.stdcall, dynlib: lib, importc: "mysql_thread_safe".} +proc embedded*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_embedded".} +proc manager_init*(con: PMANAGER): PMANAGER{.stdcall, dynlib: lib, + importc: "mysql_manager_init".} +proc manager_connect*(con: PMANAGER, host: cstring, user: cstring, + passwd: cstring, port: cuint): PMANAGER{.stdcall, + dynlib: lib, importc: "mysql_manager_connect".} +proc manager_close*(con: PMANAGER){.stdcall, dynlib: lib, + importc: "mysql_manager_close".} +proc manager_command*(con: PMANAGER, cmd: cstring, cmd_len: cint): cint{. + stdcall, dynlib: lib, importc: "mysql_manager_command".} +proc manager_fetch_line*(con: PMANAGER, res_buf: cstring, res_buf_size: cint): cint{. + stdcall, dynlib: lib, importc: "mysql_manager_fetch_line".} +proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_read_query_result".} +proc stmt_init*(MySQL: PMySQL): PSTMT{.stdcall, dynlib: lib, importc: "mysql_stmt_init".} +proc stmt_prepare*(stmt: PSTMT, query: cstring, len: int): cint{.stdcall, + dynlib: lib, importc: "mysql_stmt_prepare".} +proc stmt_execute*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_execute".} +proc stmt_fetch*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_fetch".} +proc stmt_fetch_column*(stmt: PSTMT, `bind`: PBIND, column: cuint, offset: int): cint{. + stdcall, dynlib: lib, importc: "mysql_stmt_fetch_column".} +proc stmt_store_result*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_store_result".} +proc stmt_param_count*(stmt: PSTMT): int{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_count".} +proc stmt_attr_set*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_set".} +proc stmt_attr_get*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_get".} +proc stmt_bind_param*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_param".} +proc stmt_bind_result*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_result".} +proc stmt_close*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_close".} +proc stmt_reset*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_reset".} +proc stmt_free_result*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_free_result".} +proc stmt_send_long_data*(stmt: PSTMT, param_number: cuint, data: cstring, + len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_send_long_data".} +proc stmt_result_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_result_metadata".} +proc stmt_param_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_metadata".} +proc stmt_errno*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, + importc: "mysql_stmt_errno".} +proc stmt_error*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_error".} +proc stmt_sqlstate*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_sqlstate".} +proc stmt_row_seek*(stmt: PSTMT, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_stmt_row_seek".} +proc stmt_row_tell*(stmt: PSTMT): ROW_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_stmt_row_tell".} +proc stmt_data_seek*(stmt: PSTMT, offset: my_ulonglong){.stdcall, dynlib: lib, + importc: "mysql_stmt_data_seek".} +proc stmt_num_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_num_rows".} +proc stmt_affected_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_affected_rows".} +proc stmt_insert_id*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_insert_id".} +proc stmt_field_count*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, + importc: "mysql_stmt_field_count".} +proc commit*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_commit".} +proc rollback*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rollback".} +proc autocommit*(MySQL: PMySQL, auto_mode: my_bool): my_bool{.stdcall, dynlib: lib, + importc: "mysql_autocommit".} +proc more_results*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_more_results".} +proc next_result*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_next_result".} +proc close*(sock: PMySQL){.stdcall, dynlib: lib, importc: "mysql_close".} + # status return codes +const + NO_DATA* = 100 + DATA_TRUNCATED* = 101 + +proc reload*(x: PMySQL): cint +when defined(USE_OLD_FUNCTIONS): + proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall, + dynlib: lib, importc: "mysql_connect".} + proc create_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_create_db".} + proc drop_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_drop_db".} +proc net_safe_read*(MySQL: PMySQL): cuint{.cdecl, dynlib: lib, importc: "net_safe_read".} + +proc IS_PRI_KEY(n: int32): bool = + result = (n and PRI_KEY_FLAG) != 0 + +proc IS_NOT_NULL(n: int32): bool = + result = (n and NOT_NULL_FLAG) != 0 + +proc IS_BLOB(n: int32): bool = + result = (n and BLOB_FLAG) != 0 + +proc IS_NUM_FIELD(f: Pst_mysql_field): bool = + result = (f.flags and NUM_FLAG) != 0 + +proc IS_NUM(t: Enum_field_types): bool = + result = (t <= FIELD_TYPE_INT24) or (t == FIELD_TYPE_YEAR) or + (t == FIELD_TYPE_NEWDECIMAL) + +proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool = + result = (f.ftype <= FIELD_TYPE_INT24) and + ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or + (f.ftype == FIELD_TYPE_YEAR) + +proc reload(x: PMySQL): cint = + result = refresh(x, REFRESH_GRANT) + +{.pop.} +when defined(nimHasStyleChecks): + {.pop.} diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_impl.nim b/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_impl.nim new file mode 100644 index 0000000..f4d8637 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_impl.nim @@ -0,0 +1,445 @@ +import std/asyncdispatch +import std/times +import std/strutils +import std/strformat +import std/json +import ../../error +import ../../models/database_types +import ../../models/mysql/mysql_types +import ../../prepared_param +import ./mysql_rdb +import ./mysql_lib + + +proc rawExec(conn:PMySQL, query: string, args: seq[string]) = + assert conn.ping == 0 + + var stmt = mysql_rdb.stmt_init(conn) + if stmt.isNil: + mysql_rdb.close(conn) + dbError("mysql_stmt_init() failed") + + var q = dbFormat(query, args) + if realQuery(conn, q.cstring, q.len) != 0'i32: dbError(conn) + + +proc rawExec(conn:PMySQL, query: string, args: MysqlParams) = + assert conn.ping == 0 + + var stmt = mysql_rdb.stmt_init(conn) + if stmt.isNil: + mysql_rdb.close(conn) + dbError("mysql_stmt_init() failed") + + var q = dbFormat(conn, query, args) + if realQuery(conn, q.cstring, q.len) != 0'i32: dbError(conn) + + +proc prepareStmt*(conn: PMySQL, sql: string, timeout: int): Future[PSTMT] {.async.} = + assert(not conn.isNil, "Database not connected.") + await sleepAsync(0) + result = mysql_rdb.stmt_init(conn) + if result.isNil: + dbError(conn) + if mysql_rdb.stmt_prepare(result, sql.cstring, sql.len) != 0: + let errmsg = $mysql_rdb.stmt_error(result) + discard mysql_rdb.stmt_close(result) + raise newException(DbError, errmsg) + + +proc bindStmtParams(stmt: PSTMT, args: seq[PreparedParam]) = + if mysql_rdb.stmt_param_count(stmt) != args.len: + raise newException(DbError, "Prepared statement parameter count mismatch.") + + if args.len == 0: + return + + var binds = newSeq[BIND](args.len) + var values = newSeq[string](args.len) + var lengths = newSeq[culong](args.len) + var nullFlags = newSeq[my_bool](args.len) + var errorFlags = newSeq[my_bool](args.len) + + for i, arg in args: + if arg.isNull: + nullFlags[i] = true + binds[i].buffer_type = TYPE_NULL + binds[i].is_null = addr nullFlags[i] + binds[i].error = addr errorFlags[i] + continue + + values[i] = arg.value + lengths[i] = values[i].len.culong + binds[i].buffer_type = TYPE_STRING + if values[i].len > 0: + binds[i].buffer = cast[pointer](values[i].cstring) + else: + binds[i].buffer = nil + binds[i].buffer_length = values[i].len.culong + binds[i].length = addr lengths[i] + binds[i].is_null = addr nullFlags[i] + binds[i].error = addr errorFlags[i] + + if mysql_rdb.stmt_bind_param(stmt, binds[0].addr): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + + +proc bindStmtResults( + stmt: PSTMT, + metadata: PRES, + resultBinds: MysqlResultBindCache +) = + let cols = int(mysql_rdb.num_fields(metadata)) + if resultBinds.binds.len != cols: + resultBinds.binds = newSeq[BIND](cols) + resultBinds.buffers = newSeq[string](cols) + resultBinds.lengths = newSeq[culong](cols) + resultBinds.nullFlags = newSeq[my_bool](cols) + resultBinds.errorFlags = newSeq[my_bool](cols) + for i in 0 ..< cols: + let field = mysql_rdb.fetch_field_direct(metadata, cast[mysql_rdb.cuint](i)) + var bufferLen = int(field.len) + if bufferLen < 4096: + bufferLen = 4096 + resultBinds.buffers[i] = newString(bufferLen) + else: + for i in 0 ..< cols: + resultBinds.lengths[i] = 0 + resultBinds.nullFlags[i] = false + resultBinds.errorFlags[i] = false + + for i in 0 ..< cols: + resultBinds.binds[i].buffer_type = TYPE_STRING + resultBinds.binds[i].buffer = if resultBinds.buffers[i].len > 0: cast[pointer](resultBinds.buffers[i].cstring) else: nil + resultBinds.binds[i].buffer_length = resultBinds.buffers[i].len.culong + resultBinds.binds[i].length = addr resultBinds.lengths[i] + resultBinds.binds[i].is_null = addr resultBinds.nullFlags[i] + resultBinds.binds[i].error = addr resultBinds.errorFlags[i] + + if cols > 0 and mysql_rdb.stmt_bind_result(stmt, resultBinds.binds[0].addr): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + + +proc refetchTruncatedColumns( + stmt: PSTMT, + resultBinds: MysqlResultBindCache +) = + for i in 0 ..< resultBinds.binds.len: + if not resultBinds.errorFlags[i]: + continue + let needed = max(int(resultBinds.lengths[i]), resultBinds.buffers[i].len) + if needed <= 0: + continue + resultBinds.buffers[i] = newString(needed) + resultBinds.binds[i].buffer = cast[pointer](resultBinds.buffers[i].cstring) + resultBinds.binds[i].buffer_length = needed.culong + if mysql_rdb.stmt_fetch_column(stmt, resultBinds.binds[i].addr, cast[mysql_rdb.cuint](i), 0) != 0: + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + + +proc execPreparedStmt*(conn: PMySQL, stmt: PSTMT, args: seq[PreparedParam], timeout: int) {.async.} = + assert(not conn.isNil, "Database not connected.") + await sleepAsync(0) + if mysql_rdb.stmt_reset(stmt): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + if mysql_rdb.stmt_free_result(stmt): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + bindStmtParams(stmt, args) + if mysql_rdb.stmt_execute(stmt) != 0: + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + if mysql_rdb.stmt_free_result(stmt): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + + +proc queryPreparedStmt*( + conn: PMySQL, + stmt: PSTMT, + args: seq[PreparedParam], + timeout: int, + resultBinds: MysqlResultBindCache +): Future[(seq[database_types.Row], DbRows)] {.async.} = + assert(not conn.isNil, "Database not connected.") + await sleepAsync(0) + if mysql_rdb.stmt_reset(stmt): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + if mysql_rdb.stmt_free_result(stmt): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + bindStmtParams(stmt, args) + if mysql_rdb.stmt_execute(stmt) != 0: + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + + var dbRows: DbRows + var rows = newSeq[seq[string]]() + let metadata = mysql_rdb.stmt_result_metadata(stmt) + if metadata.isNil: + if mysql_rdb.stmt_free_result(stmt): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + return (rows, dbRows) + + defer: + mysql_rdb.free_result(metadata) + if mysql_rdb.stmt_free_result(stmt): + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + + if mysql_rdb.stmt_store_result(stmt) != 0: + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + + let cols = int(mysql_rdb.num_fields(metadata)) + var baseColumns: DbColumns + setColumnInfo(baseColumns, metadata, cols) + bindStmtResults(stmt, metadata, resultBinds) + + while true: + let fetchRes = mysql_rdb.stmt_fetch(stmt) + if fetchRes == 100: + break + if fetchRes notin {0, 101}: + raise newException(DbError, $mysql_rdb.stmt_error(stmt)) + if fetchRes == 101: + refetchTruncatedColumns(stmt, resultBinds) + + var rowColumns = baseColumns + var row = newSeq[string](cols) + for i in 0 ..< cols: + if resultBinds.nullFlags[i]: + rowColumns[i].typ.kind = dbNull + row[i] = "" + else: + let length = min(int(resultBinds.lengths[i]), resultBinds.buffers[i].len) + if length <= 0: + row[i] = "" + else: + row[i] = resultBinds.buffers[i][0 ..< length] + rows.add(row) + dbRows.add(rowColumns) + + return (rows, dbRows) + + +proc closePreparedStmt*(stmt: PSTMT) = + if stmt.isNil: + return + discard mysql_rdb.stmt_close(stmt) + + +proc query*(db:PMySQL, query: string, args: seq[string], timeout:int):Future[(seq[database_types.Row], DbRows)] {.async.} = + assert db.ping == 0 + var dbRows: DbRows + var rows = newSeq[seq[string]]() + var lines = 0 + + rawExec(db, query, args) + + var sqlres = mysql_rdb.useResult(db) + let calledAt = getTime().toUnix() + var dbColumns: DbColumns + let cols = int(mysql_rdb.numFields(sqlres)) + + while true: + if getTime().toUnix() >= calledAt + timeout: + return + await sleepAsync(0) + var row: mysql_rdb.Row + var baseRow = newSeq[string](cols) + setColumnInfo(dbColumns, sqlres, cols) + row = mysql_rdb.fetchRow(sqlres) + if row == nil: break + for i in 0..= calledAt + timeout: + return + await sleepAsync(0) + var row: mysql_rdb.Row + var baseRow = newSeq[string](cols) + row = mysql_rdb.fetchRow(sqlres) + if row == nil: break + for i in 0..= calledAt + timeout: + return + await sleepAsync(0) + var row: mysql_rdb.Row + var baseRow = newSeq[string](cols) + setColumnInfo(dbColumns, sqlres, cols) + row = mysql_rdb.fetchRow(sqlres) + if row == nil: break + for i in 0..= calledAt + timeout: + return + await sleepAsync(0) + var row: mysql_rdb.Row + setColumnInfo(dbColumns, sqlres, cols) + for column in dbColumns: + columns.add(column.name) + row = mysql_rdb.fetchRow(sqlres) + break + free_result(sqlres) + return columns + + +proc getColumnTypes*(db:PMySQL, database, table:string, timeout:int):Future[seq[database_types.Row]] {.async.} = + assert db.ping == 0 + + let sql = &"SELECT `COLUMN_NAME`, `DATA_TYPE` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE TABLE_SCHEMA = '{database}' AND TABLE_NAME = '{table}'" + return queryPlain(db, sql, @[], timeout).await diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_lib.nim b/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_lib.nim new file mode 100644 index 0000000..1829fce --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_lib.nim @@ -0,0 +1,224 @@ +import std/json +import ../../error +import ../../models/database_types +import ./mysql_rdb + + +# type InstantRow* = object ## a handle that can be used to get a row's +# ## column text on demand +# row*: cstringArray +# len: int + +proc dbError*(db: PMySQL) {.noreturn.} = + ## raises a DbError exception. + var e: ref DbError + new(e) + e.msg = $db.error + raise e + +# proc checkError*(db: PMySQL) = +# ## Raises a DbError exception. +# var message = $db.error +# if message.len > 0: +# raise newException(DbError, $message) + +proc setTypeName(t: var DbType; f: PFIELD) = + t.name = $f.name + t.maxReprLen = Natural(f.max_length) + if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true + case f.ftype + of TYPE_DECIMAL: + t.kind = dbDecimal + of TYPE_TINY: + t.kind = dbInt + t.size = 1 + of TYPE_SHORT: + t.kind = dbInt + t.size = 2 + of TYPE_LONG: + t.kind = dbInt + t.size = 4 + of TYPE_FLOAT: + t.kind = dbFloat + t.size = 4 + of TYPE_DOUBLE: + t.kind = dbFloat + t.size = 8 + of TYPE_NULL: + t.kind = dbNull + of TYPE_TIMESTAMP: + t.kind = dbTimestamp + of TYPE_LONGLONG: + t.kind = dbInt + t.size = 8 + of TYPE_INT24: + t.kind = dbInt + t.size = 3 + of TYPE_DATE: + t.kind = dbDate + of TYPE_TIME: + t.kind = dbTime + of TYPE_DATETIME: + t.kind = dbDatetime + of TYPE_YEAR: + t.kind = dbDate + of TYPE_NEWDATE: + t.kind = dbDate + of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING: + t.kind = dbVarchar + of TYPE_BIT: + t.kind = dbBit + of TYPE_NEWDECIMAL: + t.kind = dbDecimal + of TYPE_ENUM: t.kind = dbEnum + of TYPE_SET: t.kind = dbSet + of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB, + TYPE_BLOB: t.kind = dbBlob + of TYPE_GEOMETRY: + t.kind = dbGeometry + +proc setColumnInfo*(columns: var DbColumns; res: PRES; L: int) = + setLen(columns, L) + for i in 0..affected_rows after statement execution + insert_id*: my_ulonglong + read_row_func*: proc (stmt: Pst_mysql_stmt, row: PPByte): cint{.cdecl.} + stmt_id*: int # Id for prepared statement + flags*: int # i.e. type of cursor to open + prefetch_rows*: int # number of rows per one COM_FETCH + server_status*: cuint # Copied from mysql->server_status after execute/fetch to know + # server-side cursor status for this statement. + last_errno*: cuint # error code + param_count*: cuint # input parameter count + field_count*: cuint # number of columns in result set + state*: Enum_mysql_stmt_state # statement state + last_error*: array[0..(ERRMSG_SIZE) - 1, char] # error message + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + send_types_to_server*: my_bool # Types of input parameters should be sent to server + bind_param_done*: my_bool # input buffers were supplied + bind_result_done*: char # output buffers were supplied + unbuffered_fetch_cancelled*: my_bool + update_max_length*: my_bool + + STMT* = St_mysql_stmt + +# Enum_stmt_attr_type* = enum +# STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS +# {.deprecated: [Tst_dynamic_array: St_dynamic_array, Tst_mysql_options: St_mysql_options, +# TDYNAMIC_ARRAY: DYNAMIC_ARRAY, Tprotocol_type: Protocol_type, +# Trpl_type: Rpl_type, Tcharset_info_st: Charset_info_st, +# TCHARSET_INFO: CHARSET_INFO, Tcharacter_set: Character_set, +# TMY_CHARSET_INFO: MY_CHARSET_INFO, Tst_mysql: St_mysql, +# Tst_mysql_methods: St_mysql_methods, TMySql: MySql, +# Tst_mysql_res: St_mysql_res, TMETHODS: METHODS, TRES: RES, +# Tst_mysql_manager: St_mysql_manager, TMANAGER: MANAGER, +# Tst_mysql_parameters: St_mysql_parameters, TPARAMETERS: PARAMETERS, +# Tenum_mysql_stmt_state: Enum_mysql_stmt_state, +# Tst_mysql_bind: St_mysql_bind, TBIND: BIND, Tst_mysql_stmt: St_mysql_stmt, +# TSTMT: STMT, Tenum_stmt_attr_type: Enum_stmt_attr_type, +# Tstatus: Status].} + +# proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. +# cdecl, dynlib: lib, importc: "mysql_server_init".} +# proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} +# # mysql_server_init/end need to be called when using libmysqld or +# # libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so +# # you don't need to call it explicitly; but you need to call +# # mysql_server_end() to free memory). The names are a bit misleading +# # (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general +# # names which suit well whether you're using libmysqld or libmysqlclient. We +# # intend to promote these aliases over the mysql_server* ones. +# proc library_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. +# cdecl, dynlib: lib, importc: "mysql_server_init".} +# proc library_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} +# proc get_parameters*(): PPARAMETERS{.stdcall, dynlib: lib, +# importc: "mysql_get_parameters".} +# # Set up and bring down a thread; these function should be called +# # for each thread in an application which opens at least one MySQL +# # connection. All uses of the connection(s) should be between these +# # function calls. +# proc thread_init*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_thread_init".} +# proc thread_end*(){.stdcall, dynlib: lib, importc: "mysql_thread_end".} +# # Functions to get information from the MYSQL and MYSQL_RES structures +# # Should definitely be used if one uses shared libraries. +# proc num_rows*(res: PRES): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_num_rows".} +proc num_fields*(res: PRES): cuint{.stdcall, dynlib: lib, + importc: "mysql_num_fields".} +# proc eof*(res: PRES): my_bool{.stdcall, dynlib: lib, importc: "mysql_eof".} +proc fetch_field_direct*(res: PRES, fieldnr: cuint): PFIELD{.stdcall, + dynlib: lib, importc: "mysql_fetch_field_direct".} +# proc fetch_fields*(res: PRES): PFIELD{.stdcall, dynlib: lib, +# importc: "mysql_fetch_fields".} +# proc row_tell*(res: PRES): ROW_OFFSET{.stdcall, dynlib: lib, +# importc: "mysql_row_tell".} +# proc field_tell*(res: PRES): FIELD_OFFSET{.stdcall, dynlib: lib, +# importc: "mysql_field_tell".} +# proc field_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, +# importc: "mysql_field_count".} +# proc affected_rows*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_affected_rows".} +# proc insert_id*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_insert_id".} +# proc errno*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, importc: "mysql_errno".} +proc error*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_error".} +# proc sqlstate*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_sqlstate".} +# proc warning_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, +# importc: "mysql_warning_count".} +# proc info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_info".} +# proc thread_id*(MySQL: PMySQL): int{.stdcall, dynlib: lib, importc: "mysql_thread_id".} +# proc character_set_name*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, +# importc: "mysql_character_set_name".} +# proc set_character_set*(MySQL: PMySQL, csname: cstring): int32{.stdcall, dynlib: lib, +# importc: "mysql_set_character_set".} +proc init*(MySQL: PMySQL): PMySQL{.stdcall, dynlib: lib, importc: "mysql_init".} +proc init*(MySQL: var PMySQL){.stdcall, dynlib: lib, importc: "mysql_init".} +# proc ssl_set*(MySQL: PMySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring, +# cipher: cstring): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_ssl_set".} +# proc change_user*(MySQL: PMySQL, user: cstring, passwd: cstring, db: cstring): my_bool{. +# stdcall, dynlib: lib, importc: "mysql_change_user".} +proc real_connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: int): PMySQL{.stdcall, dynlib: lib, + importc: "mysql_real_connect".} +# proc real_connect_start*(ret: ptr PMySQL, mysql: PMySQL, host: cstring, user: cstring, passwd: cstring, +# db: cstring, port: cuint, unix_socket: cstring, +# clientflag: culong): cint{.stdcall, dynlib: lib, +# importc: "mysql_real_connect_start".} +# proc fetch_row_nonblocking*(MYSQL_RES: PRES, MYSQL_ROW: PROW): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_fetch_row_nonblocking".} +# proc free_result_nonblocking*(MYSQL_RES: PRES): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_free_result_nonblocking".} +# proc fetch_next_result_nonblocking*(MySQL: PMySQL): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_next_result_nonblocking".} +# proc real_query_nonblocking*(MySQL: PMySQL, q: cstring, length: int): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_real_query_nonblocking".} +# proc store_result_nonblocking*(MySQL: PMySQL, MYSQL_RES: ptr PRES): NetAsyncStatus{.stdcall, dynlib: lib, +# importc: "mysql_store_result_nonblocking".} +# proc select_db*(MySQL: PMySQL, db: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_select_db".} +# proc query*(MySQL: PMySQL, q: cstring): cint{.stdcall, dynlib: lib, importc: "mysql_query".} +# proc send_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, +# importc: "mysql_send_query".} +proc real_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_real_query".} +# proc store_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, +# importc: "mysql_store_result".} +proc use_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, importc: "mysql_use_result".} +# # perform query on master +# proc master_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_master_query".} +# proc master_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, +# dynlib: lib, importc: "mysql_master_send_query".} +# # perform query on slave +# proc slave_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_slave_query".} +# proc slave_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, +# dynlib: lib, importc: "mysql_slave_send_query".} +# proc get_character_set_info*(MySQL: PMySQL, charset: PMY_CHARSET_INFO){.stdcall, +# dynlib: lib, importc: "mysql_get_character_set_info".} +# # local infile support +# const +# LOCAL_INFILE_ERROR_LEN* = 512 + +# # procedure mysql_set_local_infile_handler(mysql:PMYSQL; local_infile_init:function (para1:Ppointer; para2:Pchar; para3:pointer):longint; local_infile_read:function (para1:pointer; para2:Pchar; para3:dword):longint; local_infile_end:procedure (_pa +# # para6:pointer);cdecl;external mysqllib name 'mysql_set_local_infile_handler'; + +# proc set_local_infile_default*(MySQL: PMySQL){.cdecl, dynlib: lib, +# importc: "mysql_set_local_infile_default".} +# # enable/disable parsing of all queries to decide if they go on master or +# # slave +# proc enable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, +# importc: "mysql_enable_rpl_parse".} +# proc disable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, +# importc: "mysql_disable_rpl_parse".} +# # get the value of the parse flag +# proc rpl_parse_enabled*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, +# importc: "mysql_rpl_parse_enabled".} +# # enable/disable reads from master +# proc enable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, +# importc: "mysql_enable_reads_from_master".} +# proc disable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, importc: "mysql_disable_reads_from_master".} +# # get the value of the master read flag +# proc reads_from_master_enabled*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_reads_from_master_enabled".} +# proc rpl_query_type*(q: cstring, length: cint): Rpl_type{.stdcall, dynlib: lib, +# importc: "mysql_rpl_query_type".} +# # discover the master and its slaves +# proc rpl_probe*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rpl_probe".} +# # set the master, close/free the old one, if it is not a pivot +# proc set_master*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. +# stdcall, dynlib: lib, importc: "mysql_set_master".} +# proc add_slave*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. +# stdcall, dynlib: lib, importc: "mysql_add_slave".} +# proc shutdown*(MySQL: PMySQL, shutdown_level: Enum_shutdown_level): cint{.stdcall, +# dynlib: lib, importc: "mysql_shutdown".} +# proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, +# importc: "mysql_dump_debug_info".} +# proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib, +# importc: "mysql_refresh".} +# proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".} +# proc set_server_option*(MySQL: PMySQL, option: Enum_mysql_set_option): cint{.stdcall, +# dynlib: lib, importc: "mysql_set_server_option".} +proc ping*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_ping".} +# proc stat*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_stat".} +# proc get_server_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, +# importc: "mysql_get_server_info".} +# proc get_client_info*(): cstring{.stdcall, dynlib: lib, +# importc: "mysql_get_client_info".} +# proc get_client_version*(): int{.stdcall, dynlib: lib, +# importc: "mysql_get_client_version".} +# proc get_host_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, +# importc: "mysql_get_host_info".} +# proc get_server_version*(MySQL: PMySQL): int{.stdcall, dynlib: lib, +# importc: "mysql_get_server_version".} +# proc get_proto_info*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, +# importc: "mysql_get_proto_info".} +# proc list_dbs*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, +# importc: "mysql_list_dbs".} +# proc list_tables*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, +# importc: "mysql_list_tables".} +# proc list_processes*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, +# importc: "mysql_list_processes".} +# proc options*(MySQL: PMySQL, option: Option, arg: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_options".} +proc free_result*(result: PRES){.stdcall, dynlib: lib, + importc: "mysql_free_result".} +# proc data_seek*(result: PRES, offset: my_ulonglong){.stdcall, dynlib: lib, +# importc: "mysql_data_seek".} +# proc row_seek*(result: PRES, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, +# dynlib: lib, importc: "mysql_row_seek".} +# proc field_seek*(result: PRES, offset: FIELD_OFFSET): FIELD_OFFSET{.stdcall, +# dynlib: lib, importc: "mysql_field_seek".} +proc fetch_row*(result: PRES): ROW{.stdcall, dynlib: lib, + importc: "mysql_fetch_row".} +# proc fetch_lengths*(result: PRES): ptr int{.stdcall, dynlib: lib, +# importc: "mysql_fetch_lengths".} +# proc fetch_field*(result: PRES): PFIELD{.stdcall, dynlib: lib, +# importc: "mysql_fetch_field".} +# proc list_fields*(MySQL: PMySQL, table: cstring, wild: cstring): PRES{.stdcall, +# dynlib: lib, importc: "mysql_list_fields".} +# proc escape_string*(fto: cstring, `from`: cstring, from_length: int): int{. +# stdcall, dynlib: lib, importc: "mysql_escape_string".} +# proc hex_string*(fto: cstring, `from`: cstring, from_length: int): int{.stdcall, +# dynlib: lib, importc: "mysql_hex_string".} +proc real_escape_string*(MySQL: PMySQL, fto: cstring, `from`: cstring, len: int): int{. + stdcall, dynlib: lib, importc: "mysql_real_escape_string".} +# proc debug*(debug: cstring){.stdcall, dynlib: lib, importc: "mysql_debug".} +# # function mysql_odbc_escape_string(mysql:PMYSQL; fto:Pchar; to_length:dword; from:Pchar; from_length:dword; +# # param:pointer; extend_buffer:function (para1:pointer; to:Pchar; length:Pdword):Pchar):Pchar;stdcall;external mysqllib name 'mysql_odbc_escape_string'; +# proc myodbc_remove_escape*(MySQL: PMySQL, name: cstring){.stdcall, dynlib: lib, +# importc: "myodbc_remove_escape".} +# proc thread_safe*(): cuint{.stdcall, dynlib: lib, importc: "mysql_thread_safe".} +# proc embedded*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_embedded".} +# proc manager_init*(con: PMANAGER): PMANAGER{.stdcall, dynlib: lib, +# importc: "mysql_manager_init".} +# proc manager_connect*(con: PMANAGER, host: cstring, user: cstring, +# passwd: cstring, port: cuint): PMANAGER{.stdcall, +# dynlib: lib, importc: "mysql_manager_connect".} +# proc manager_close*(con: PMANAGER){.stdcall, dynlib: lib, +# importc: "mysql_manager_close".} +# proc manager_command*(con: PMANAGER, cmd: cstring, cmd_len: cint): cint{. +# stdcall, dynlib: lib, importc: "mysql_manager_command".} +# proc manager_fetch_line*(con: PMANAGER, res_buf: cstring, res_buf_size: cint): cint{. +# stdcall, dynlib: lib, importc: "mysql_manager_fetch_line".} +# proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_read_query_result".} +proc stmt_init*(MySQL: PMySQL): PSTMT{.stdcall, dynlib: lib, importc: "mysql_stmt_init".} +proc stmt_prepare*(stmt: PSTMT, query: cstring, len: int): cint{.stdcall, + dynlib: lib, importc: "mysql_stmt_prepare".} +proc stmt_execute*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_execute".} +proc stmt_fetch*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_fetch".} +proc stmt_fetch_column*(stmt: PSTMT, `bind`: PBIND, column: cuint, offset: int): cint{. + stdcall, dynlib: lib, importc: "mysql_stmt_fetch_column".} +proc stmt_store_result*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_store_result".} +proc stmt_param_count*(stmt: PSTMT): int{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_count".} +proc stmt_bind_param*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_param".} +proc stmt_bind_result*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_result".} +proc stmt_close*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_close".} +proc stmt_reset*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_reset".} +proc stmt_free_result*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_free_result".} +# proc stmt_send_long_data*(stmt: PSTMT, param_number: cuint, data: cstring, +# len: int): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_stmt_send_long_data".} +proc stmt_result_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_result_metadata".} +# proc stmt_param_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, +# importc: "mysql_stmt_param_metadata".} +# proc stmt_errno*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, +# importc: "mysql_stmt_errno".} +proc stmt_error*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_error".} +# proc stmt_sqlstate*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, +# importc: "mysql_stmt_sqlstate".} +# proc stmt_row_seek*(stmt: PSTMT, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, +# dynlib: lib, importc: "mysql_stmt_row_seek".} +# proc stmt_row_tell*(stmt: PSTMT): ROW_OFFSET{.stdcall, dynlib: lib, +# importc: "mysql_stmt_row_tell".} +# proc stmt_data_seek*(stmt: PSTMT, offset: my_ulonglong){.stdcall, dynlib: lib, +# importc: "mysql_stmt_data_seek".} +# proc stmt_num_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_stmt_num_rows".} +# proc stmt_affected_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_stmt_affected_rows".} +# proc stmt_insert_id*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, +# importc: "mysql_stmt_insert_id".} +# proc stmt_field_count*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, +# importc: "mysql_stmt_field_count".} +# proc commit*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_commit".} +# proc rollback*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rollback".} +# proc autocommit*(MySQL: PMySQL, auto_mode: my_bool): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_autocommit".} +# proc more_results*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, +# importc: "mysql_more_results".} +# proc next_result*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_next_result".} +proc close*(sock: PMySQL){.stdcall, dynlib: lib, importc: "mysql_close".} + # status return codes +# const +# NO_DATA* = 100 +# DATA_TRUNCATED* = 101 + +# proc reload*(x: PMySQL): cint +# when defined(USE_OLD_FUNCTIONS): +# proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall, +# dynlib: lib, importc: "mysql_connect".} +# proc create_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_create_db".} +# proc drop_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, +# importc: "mysql_drop_db".} +# proc net_safe_read*(MySQL: PMySQL): cuint{.cdecl, dynlib: lib, importc: "net_safe_read".} + +# proc IS_PRI_KEY(n: int32): bool = +# result = (n and PRI_KEY_FLAG) != 0 + +# proc IS_NOT_NULL(n: int32): bool = +# result = (n and NOT_NULL_FLAG) != 0 + +# proc IS_BLOB(n: int32): bool = +# result = (n and BLOB_FLAG) != 0 + +# proc IS_NUM_FIELD(f: Pst_mysql_field): bool = +# result = (f.flags and NUM_FLAG) != 0 + +# proc IS_NUM(t: Enum_field_types): bool = +# result = (t <= FIELD_TYPE_INT24) or (t == FIELD_TYPE_YEAR) or +# (t == FIELD_TYPE_NEWDECIMAL) + +# proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool = +# result = (f.ftype <= FIELD_TYPE_INT24) and +# ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or +# (f.ftype == FIELD_TYPE_YEAR) + +# proc reload(x: PMySQL): cint = +# result = refresh(x, REFRESH_GRANT) + +# {.pop.} +# when defined(nimHasStyleChecks): +# {.pop.} diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_rdb.nim.bk b/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_rdb.nim.bk new file mode 100644 index 0000000..3f21152 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/mysql/mysql_rdb.nim.bk @@ -0,0 +1,1157 @@ +import ../../../../utils + +{.push, callconv: cdecl.} +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(unix): + when defined(macosx): + const lib = "(libmysqlclient|libmariadb)(|.21|.20|.19|.18|.17|.16|.15).dylib" + else: + const osName = getOsName() + when osName == "alpine": + const lib = "(libmysqlclient|libmariadb).so(|.3)" + else: + const lib = "(libmysqlclient|libmariadb).so(|.21|.20|.19|.18|.17|.16|.15)" +when defined(windows): + const lib = "(libmysql.dll|libmariadb.dll)" + +type + my_bool* = bool + Pmy_bool* = ptr my_bool + PVIO* = pointer + Pgptr* = ptr gptr + gptr* = cstring + Pmy_socket* = ptr my_socket + my_socket* = cint + PPByte* = pointer + cuint* = cint + +type NetAsyncStatus* = enum + NET_ASYNC_COMPLETE + NET_ASYNC_NOT_READY + NET_ASYNC_ERROR + NET_ASYNC_COMPLETE_NO_MORE_RESULTS + +# ------------ Start of declaration in "mysql_com.h" --------------------- +# +# ** Common definition between mysql server & client +# +# Field/table name length + +const + NAME_LEN* = 64 + HOSTNAME_LENGTH* = 60 + USERNAME_LENGTH* = 16 + SERVER_VERSION_LENGTH* = 60 + SQLSTATE_LENGTH* = 5 + LOCAL_HOST* = "localhost" + LOCAL_HOST_NAMEDPIPE* = '.' + +const + NAMEDPIPE* = "MySQL" + SERVICENAME* = "MySQL" + +type + Enum_server_command* = enum + COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB, + COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO, + COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME, + COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP, + COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE, + COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION, + COM_STMT_FETCH, COM_END +{.deprecated: [Tenum_server_command: Enum_server_command].} + +const + SCRAMBLE_LENGTH* = 20 # Length of random string sent by server on handshake; + # this is also length of obfuscated password, + # received from client + SCRAMBLE_LENGTH_323* = 8 # length of password stored in the db: + # new passwords are preceded with '*' + SCRAMBLED_PASSWORD_CHAR_LENGTH* = SCRAMBLE_LENGTH * 2 + 1 + SCRAMBLED_PASSWORD_CHAR_LENGTH_323* = SCRAMBLE_LENGTH_323 * 2 + NOT_NULL_FLAG* = 1 # Field can't be NULL + PRI_KEY_FLAG* = 2 # Field is part of a primary key + UNIQUE_KEY_FLAG* = 4 # Field is part of a unique key + MULTIPLE_KEY_FLAG* = 8 # Field is part of a key + BLOB_FLAG* = 16 # Field is a blob + UNSIGNED_FLAG* = 32 # Field is unsigned + ZEROFILL_FLAG* = 64 # Field is zerofill + BINARY_FLAG* = 128 # Field is binary + # The following are only sent to new clients + ENUM_FLAG* = 256 # field is an enum + AUTO_INCREMENT_FLAG* = 512 # field is a autoincrement field + TIMESTAMP_FLAG* = 1024 # Field is a timestamp + SET_FLAG* = 2048 # field is a set + NO_DEFAULT_VALUE_FLAG* = 4096 # Field doesn't have default value + NUM_FLAG* = 32768 # Field is num (for clients) + PART_KEY_FLAG* = 16384 # Intern; Part of some key + GROUP_FLAG* = 32768 # Intern: Group field + UNIQUE_FLAG* = 65536 # Intern: Used by sql_yacc + BINCMP_FLAG* = 131072 # Intern: Used by sql_yacc + REFRESH_GRANT* = 1 # Refresh grant tables + REFRESH_LOG* = 2 # Start on new log file + REFRESH_TABLES* = 4 # close all tables + REFRESH_HOSTS* = 8 # Flush host cache + REFRESH_STATUS* = 16 # Flush status variables + REFRESH_THREADS* = 32 # Flush thread cache + REFRESH_SLAVE* = 64 # Reset master info and restart slave thread + REFRESH_MASTER* = 128 # Remove all bin logs in the index and truncate the index + # The following can't be set with mysql_refresh() + REFRESH_READ_LOCK* = 16384 # Lock tables for read + REFRESH_FAST* = 32768 # Intern flag + REFRESH_QUERY_CACHE* = 65536 # RESET (remove all queries) from query cache + REFRESH_QUERY_CACHE_FREE* = 0x00020000 # pack query cache + REFRESH_DES_KEY_FILE* = 0x00040000 + REFRESH_USER_RESOURCES* = 0x00080000 + CLIENT_LONG_PASSWORD* = 1 # new more secure passwords + CLIENT_FOUND_ROWS* = 2 # Found instead of affected rows + CLIENT_LONG_FLAG* = 4 # Get all column flags + CLIENT_CONNECT_WITH_DB* = 8 # One can specify db on connect + CLIENT_NO_SCHEMA* = 16 # Don't allow database.table.column + CLIENT_COMPRESS* = 32 # Can use compression protocol + CLIENT_ODBC* = 64 # Odbc client + CLIENT_LOCAL_FILES* = 128 # Can use LOAD DATA LOCAL + CLIENT_IGNORE_SPACE* = 256 # Ignore spaces before '(' + CLIENT_PROTOCOL_41* = 512 # New 4.1 protocol + CLIENT_INTERACTIVE* = 1024 # This is an interactive client + CLIENT_SSL* = 2048 # Switch to SSL after handshake + CLIENT_IGNORE_SIGPIPE* = 4096 # IGNORE sigpipes + CLIENT_TRANSACTIONS* = 8192 # Client knows about transactions + CLIENT_RESERVED* = 16384 # Old flag for 4.1 protocol + CLIENT_SECURE_CONNECTION* = 32768 # New 4.1 authentication + CLIENT_MULTI_STATEMENTS* = 65536 # Enable/disable multi-stmt support + CLIENT_MULTI_RESULTS* = 131072 # Enable/disable multi-results + CLIENT_REMEMBER_OPTIONS*: int = 1 shl 31 + SERVER_STATUS_IN_TRANS* = 1 # Transaction has started + SERVER_STATUS_AUTOCOMMIT* = 2 # Server in auto_commit mode + SERVER_STATUS_MORE_RESULTS* = 4 # More results on server + SERVER_MORE_RESULTS_EXISTS* = 8 # Multi query - next query exists + SERVER_QUERY_NO_GOOD_INDEX_USED* = 16 + SERVER_QUERY_NO_INDEX_USED* = 32 # The server was able to fulfill the clients request and opened a + # read-only non-scrollable cursor for a query. This flag comes + # in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands. + SERVER_STATUS_CURSOR_EXISTS* = 64 # This flag is sent when a read-only cursor is exhausted, in reply to + # COM_STMT_FETCH command. + SERVER_STATUS_LAST_ROW_SENT* = 128 + SERVER_STATUS_DB_DROPPED* = 256 # A database was dropped + SERVER_STATUS_NO_BACKSLASH_ESCAPES* = 512 + ERRMSG_SIZE* = 200 + NET_READ_TIMEOUT* = 30 # Timeout on read + NET_WRITE_TIMEOUT* = 60 # Timeout on write + NET_WAIT_TIMEOUT* = 8 * 60 * 60 # Wait for new query + ONLY_KILL_QUERY* = 1 + +const + MAX_TINYINT_WIDTH* = 3 # Max width for a TINY w.o. sign + MAX_SMALLINT_WIDTH* = 5 # Max width for a SHORT w.o. sign + MAX_MEDIUMINT_WIDTH* = 8 # Max width for a INT24 w.o. sign + MAX_INT_WIDTH* = 10 # Max width for a LONG w.o. sign + MAX_BIGINT_WIDTH* = 20 # Max width for a LONGLONG + MAX_CHAR_WIDTH* = 255 # Max length for a CHAR column + MAX_BLOB_WIDTH* = 8192 # Default width for blob + +type + Pst_net* = ptr St_net + St_net*{.final.} = object + vio*: PVIO + buff*: cstring + buff_end*: cstring + write_pos*: cstring + read_pos*: cstring + fd*: my_socket # For Perl DBI/dbd + max_packet*: int + max_packet_size*: int + pkt_nr*: cuint + compress_pkt_nr*: cuint + write_timeout*: cuint + read_timeout*: cuint + retry_count*: cuint + fcntl*: cint + compress*: my_bool # The following variable is set if we are doing several queries in one + # command ( as in LOAD TABLE ... FROM MASTER ), + # and do not want to confuse the client with OK at the wrong time + remain_in_buf*: int + len*: int + buf_length*: int + where_b*: int + return_status*: ptr cint + reading_or_writing*: char + save_char*: cchar + no_send_ok*: my_bool # For SPs and other things that do multiple stmts + no_send_eof*: my_bool # For SPs' first version read-only cursors + no_send_error*: my_bool # Set if OK packet is already sent, and + # we do not need to send error messages + # Pointer to query object in query cache, do not equal NULL (0) for + # queries in cache that have not stored its results yet + # $endif + last_error*: array[0..(ERRMSG_SIZE) - 1, char] + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + last_errno*: cuint + error*: char + query_cache_query*: gptr + report_error*: my_bool # We should report error (we have unreported error) + return_errno*: my_bool + + NET* = St_net + PNET* = ptr NET +{.deprecated: [Tst_net: St_net, TNET: NET].} + +const + packet_error* = - 1 + +type + Enum_field_types* = enum # For backward compatibility + TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, + TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME, + TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT, + TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248, + TYPE_TINY_BLOB = 249, TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251, + TYPE_BLOB = 252, TYPE_VAR_STRING = 253, TYPE_STRING = 254, + TYPE_GEOMETRY = 255 +{.deprecated: [Tenum_field_types: Enum_field_types].} + +const + CLIENT_MULTI_QUERIES* = CLIENT_MULTI_STATEMENTS + FIELD_TYPE_DECIMAL* = TYPE_DECIMAL + FIELD_TYPE_NEWDECIMAL* = TYPE_NEWDECIMAL + FIELD_TYPE_TINY* = TYPE_TINY + FIELD_TYPE_SHORT* = TYPE_SHORT + FIELD_TYPE_LONG* = TYPE_LONG + FIELD_TYPE_FLOAT* = TYPE_FLOAT + FIELD_TYPE_DOUBLE* = TYPE_DOUBLE + FIELD_TYPE_NULL* = TYPE_NULL + FIELD_TYPE_TIMESTAMP* = TYPE_TIMESTAMP + FIELD_TYPE_LONGLONG* = TYPE_LONGLONG + FIELD_TYPE_INT24* = TYPE_INT24 + FIELD_TYPE_DATE* = TYPE_DATE + FIELD_TYPE_TIME* = TYPE_TIME + FIELD_TYPE_DATETIME* = TYPE_DATETIME + FIELD_TYPE_YEAR* = TYPE_YEAR + FIELD_TYPE_NEWDATE* = TYPE_NEWDATE + FIELD_TYPE_ENUM* = TYPE_ENUM + FIELD_TYPE_SET* = TYPE_SET + FIELD_TYPE_TINY_BLOB* = TYPE_TINY_BLOB + FIELD_TYPE_MEDIUM_BLOB* = TYPE_MEDIUM_BLOB + FIELD_TYPE_LONG_BLOB* = TYPE_LONG_BLOB + FIELD_TYPE_BLOB* = TYPE_BLOB + FIELD_TYPE_VAR_STRING* = TYPE_VAR_STRING + FIELD_TYPE_STRING* = TYPE_STRING + FIELD_TYPE_CHAR* = TYPE_TINY + FIELD_TYPE_INTERVAL* = TYPE_ENUM + FIELD_TYPE_GEOMETRY* = TYPE_GEOMETRY + FIELD_TYPE_BIT* = TYPE_BIT # Shutdown/kill enums and constants + # Bits for THD::killable. + SHUTDOWN_KILLABLE_CONNECT* = chr(1 shl 0) + SHUTDOWN_KILLABLE_TRANS* = chr(1 shl 1) + SHUTDOWN_KILLABLE_LOCK_TABLE* = chr(1 shl 2) + SHUTDOWN_KILLABLE_UPDATE* = chr(1 shl 3) + +type + Enum_shutdown_level* = enum + SHUTDOWN_DEFAULT = 0, SHUTDOWN_WAIT_CONNECTIONS = 1, + SHUTDOWN_WAIT_TRANSACTIONS = 2, SHUTDOWN_WAIT_UPDATES = 8, + SHUTDOWN_WAIT_ALL_BUFFERS = 16, SHUTDOWN_WAIT_CRITICAL_BUFFERS = 17, + KILL_QUERY = 254, KILL_CONNECTION = 255 + Enum_cursor_type* = enum # options for mysql_set_option + CURSOR_TYPE_NO_CURSOR = 0, CURSOR_TYPE_READ_ONLY = 1, + CURSOR_TYPE_FOR_UPDATE = 2, CURSOR_TYPE_SCROLLABLE = 4 + Enum_mysql_set_option* = enum + OPTION_MULTI_STATEMENTS_ON, OPTION_MULTI_STATEMENTS_OFF +{.deprecated: [Tenum_shutdown_level: Enum_shutdown_level, + Tenum_cursor_type: Enum_cursor_type, + Tenum_mysql_set_option: Enum_mysql_set_option].} + +proc my_net_init*(net: PNET, vio: PVIO): my_bool{.cdecl, dynlib: lib, + importc: "my_net_init".} +proc my_net_local_init*(net: PNET){.cdecl, dynlib: lib, + importc: "my_net_local_init".} +proc net_end*(net: PNET){.cdecl, dynlib: lib, importc: "net_end".} +proc net_clear*(net: PNET){.cdecl, dynlib: lib, importc: "net_clear".} +proc net_realloc*(net: PNET, len: int): my_bool{.cdecl, dynlib: lib, + importc: "net_realloc".} +proc net_flush*(net: PNET): my_bool{.cdecl, dynlib: lib, importc: "net_flush".} +proc my_net_write*(net: PNET, packet: cstring, length: int): my_bool{.cdecl, + dynlib: lib, importc: "my_net_write".} +proc net_write_command*(net: PNET, command: char, header: cstring, + head_len: int, packet: cstring, length: int): my_bool{. + cdecl, dynlib: lib, importc: "net_write_command".} +proc net_real_write*(net: PNET, packet: cstring, length: int): cint{.cdecl, + dynlib: lib, importc: "net_real_write".} +proc my_net_read*(net: PNET): int{.cdecl, dynlib: lib, importc: "my_net_read".} + # The following function is not meant for normal usage + # Currently it's used internally by manager.c +type + Psockaddr* = ptr Sockaddr + Sockaddr*{.final.} = object # undefined structure +{.deprecated: [Tsockaddr: Sockaddr].} + +proc my_connect*(s: my_socket, name: Psockaddr, namelen: cuint, timeout: cuint): cint{. + cdecl, dynlib: lib, importc: "my_connect".} +type + Prand_struct* = ptr Rand_struct + Rand_struct*{.final.} = object # The following is for user defined functions + seed1*: int + seed2*: int + max_value*: int + max_value_dbl*: cdouble + + Item_result* = enum + STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT + PItem_result* = ptr Item_result + Pst_udf_args* = ptr St_udf_args + St_udf_args*{.final.} = object + arg_count*: cuint # Number of arguments + arg_type*: PItem_result # Pointer to item_results + args*: cstringArray # Pointer to item_results + lengths*: ptr int # Length of string arguments + maybe_null*: cstring # Length of string arguments + attributes*: cstringArray # Pointer to attribute name + attribute_lengths*: ptr int # Length of attribute arguments + + UDF_ARGS* = St_udf_args + PUDF_ARGS* = ptr UDF_ARGS # This holds information about the result + Pst_udf_init* = ptr St_udf_init + St_udf_init*{.final.} = object + maybe_null*: my_bool # 1 if function can return NULL + decimals*: cuint # for real functions + max_length*: int # For string functions + theptr*: cstring # free pointer for function data + const_item*: my_bool # free pointer for function data + + UDF_INIT* = St_udf_init + PUDF_INIT* = ptr UDF_INIT # Constants when using compression +{.deprecated: [Trand_stuct: Rand_struct, TItem_result: Item_result, + Tst_udf_args: St_udf_args, TUDF_ARGS: UDF_ARGS, + Tst_udf_init: St_udf_init, TUDF_INIT: UDF_INIT].} + +const + NET_HEADER_SIZE* = 4 # standard header size + COMP_HEADER_SIZE* = 3 # compression header extra size + # Prototypes to password functions + # These functions are used for authentication by client and server and + # implemented in sql/password.c + +proc randominit*(para1: Prand_struct, seed1: int, seed2: int){.cdecl, + dynlib: lib, importc: "randominit".} +proc my_rnd*(para1: Prand_struct): cdouble{.cdecl, dynlib: lib, + importc: "my_rnd".} +proc create_random_string*(fto: cstring, len: cuint, rand_st: Prand_struct){. + cdecl, dynlib: lib, importc: "create_random_string".} +proc hash_password*(fto: int, password: cstring, password_len: cuint){.cdecl, + dynlib: lib, importc: "hash_password".} +proc make_scrambled_password_323*(fto: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "make_scrambled_password_323".} +proc scramble_323*(fto: cstring, message: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "scramble_323".} +proc check_scramble_323*(para1: cstring, message: cstring, salt: int): my_bool{. + cdecl, dynlib: lib, importc: "check_scramble_323".} +proc get_salt_from_password_323*(res: ptr int, password: cstring){.cdecl, + dynlib: lib, importc: "get_salt_from_password_323".} +proc make_password_from_salt_323*(fto: cstring, salt: ptr int){.cdecl, + dynlib: lib, importc: "make_password_from_salt_323".} +proc octet2hex*(fto: cstring, str: cstring, length: cuint): cstring{.cdecl, + dynlib: lib, importc: "octet2hex".} +proc make_scrambled_password*(fto: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "make_scrambled_password".} +proc scramble*(fto: cstring, message: cstring, password: cstring){.cdecl, + dynlib: lib, importc: "scramble".} +proc check_scramble*(reply: cstring, message: cstring, hash_stage2: pointer): my_bool{. + cdecl, dynlib: lib, importc: "check_scramble".} +proc get_salt_from_password*(res: pointer, password: cstring){.cdecl, + dynlib: lib, importc: "get_salt_from_password".} +proc make_password_from_salt*(fto: cstring, hash_stage2: pointer){.cdecl, + dynlib: lib, importc: "make_password_from_salt".} + # end of password.c +proc get_tty_password*(opt_message: cstring): cstring{.cdecl, dynlib: lib, + importc: "get_tty_password".} +proc errno_to_sqlstate*(errno: cuint): cstring{.cdecl, dynlib: lib, + importc: "mysql_errno_to_sqlstate".} + # Some other useful functions +proc modify_defaults_file*(file_location: cstring, option: cstring, + option_value: cstring, section_name: cstring, + remove_option: cint): cint{.cdecl, dynlib: lib, + importc: "load_defaults".} +proc load_defaults*(conf_file: cstring, groups: cstringArray, argc: ptr cint, + argv: ptr cstringArray): cint{.cdecl, dynlib: lib, + importc: "load_defaults".} +proc my_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_init".} +proc my_thread_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_thread_init".} +proc my_thread_end*(){.cdecl, dynlib: lib, importc: "my_thread_end".} +const + NULL_LENGTH*: int = int(not (0)) # For net_store_length + +const + STMT_HEADER* = 4 + LONG_DATA_HEADER* = 6 # ------------ Stop of declaration in "mysql_com.h" ----------------------- + # $include "mysql_time.h" + # $include "mysql_version.h" + # $include "typelib.h" + # $include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */ + # var + # mysql_port : cuint;cvar;external; + # mysql_unix_port : Pchar;cvar;external; + +const + CLIENT_NET_READ_TIMEOUT* = 365 * 24 * 3600 # Timeout on read + CLIENT_NET_WRITE_TIMEOUT* = 365 * 24 * 3600 # Timeout on write + +type + Pst_mysql_field* = ptr St_mysql_field + St_mysql_field*{.final.} = object + name*: cstring # Name of column + org_name*: cstring # Original column name, if an alias + table*: cstring # Table of column if column was a field + org_table*: cstring # Org table name, if table was an alias + db*: cstring # Database for table + catalog*: cstring # Catalog for table + def*: cstring # Default value (set by mysql_list_fields) + len*: int # Width of column (create length) + max_length*: int # Max width for selected set + name_length*: cuint + org_name_length*: cuint + table_length*: cuint + org_table_length*: cuint + db_length*: cuint + catalog_length*: cuint + def_length*: cuint + flags*: cuint # Div flags + decimals*: cuint # Number of decimals in field + charsetnr*: cuint # Character set + ftype*: Enum_field_types # Type of field. See mysql_com.h for types + extension*: pointer + + FIELD* = St_mysql_field + PFIELD* = ptr FIELD + PROW* = ptr ROW # return data as array of strings + ROW* = cstringArray + PFIELD_OFFSET* = ptr FIELD_OFFSET # offset to current field + FIELD_OFFSET* = cuint +{.deprecated: [Tst_mysql_field: St_mysql_field, TFIELD: FIELD, TROW: ROW, + TFIELD_OFFSET: FIELD_OFFSET].} + +proc IS_PRI_KEY*(n: int32): bool +proc IS_NOT_NULL*(n: int32): bool +proc IS_BLOB*(n: int32): bool +proc IS_NUM*(t: Enum_field_types): bool +proc INTERNAL_NUM_FIELD*(f: Pst_mysql_field): bool +proc IS_NUM_FIELD*(f: Pst_mysql_field): bool +type + my_ulonglong* = int64 + Pmy_ulonglong* = ptr my_ulonglong + +const + COUNT_ERROR* = not (my_ulonglong(0)) + +type + Pst_mysql_rows* = ptr St_mysql_rows + St_mysql_rows*{.final.} = object + next*: Pst_mysql_rows # list of rows + data*: ROW + len*: int + + ROWS* = St_mysql_rows + PROWS* = ptr ROWS + PROW_OFFSET* = ptr ROW_OFFSET # offset to current row + ROW_OFFSET* = ROWS +{.deprecated: [Tst_mysql_rows: St_mysql_rows, TROWS: ROWS, + TROW_OFFSET: ROW_OFFSET].} + +const + ALLOC_MAX_BLOCK_TO_DROP* = 4096 + ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP* = 10 # struct for once_alloc (block) + +type + Pst_used_mem* = ptr St_used_mem + St_used_mem*{.final.} = object + next*: Pst_used_mem # Next block in use + left*: cuint # memory left in block + size*: cuint # size of block + + USED_MEM* = St_used_mem + PUSED_MEM* = ptr USED_MEM + Pst_mem_root* = ptr St_mem_root + St_mem_root*{.final.} = object + free*: PUSED_MEM # blocks with free memory in it + used*: PUSED_MEM # blocks almost without free memory + pre_alloc*: PUSED_MEM # preallocated block + min_malloc*: cuint # if block have less memory it will be put in 'used' list + block_size*: cuint # initial block size + block_num*: cuint # allocated blocks counter + # first free block in queue test counter (if it exceed + # MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list) + first_block_usage*: cuint + error_handler*: proc (){.cdecl.} + + MEM_ROOT* = St_mem_root + PMEM_ROOT* = ptr MEM_ROOT # ------------ Stop of declaration in "my_alloc.h" ---------------------- +{.deprecated: [Tst_used_mem: St_used_mem, TUSED_MEM: USED_MEM, + Tst_mem_root: St_mem_root, TMEM_ROOT: MEM_ROOT].} + +type + Pst_mysql_data* = ptr St_mysql_data + St_mysql_data*{.final.} = object + rows*: my_ulonglong + fields*: cuint + data*: PROWS + alloc*: MEM_ROOT + prev_ptr*: ptr PROWS + + DATA* = St_mysql_data + PDATA* = ptr DATA + Option* = enum + OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND, + READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME, + OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT, + OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION, + OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP, + SECURE_AUTH, REPORT_DATA_TRUNCATION, OPT_RECONNECT, + MYSQL_OPT_NONBLOCK +{.deprecated: [Tst_mysql_data: St_mysql_data, TDATA: DATA, Toption: Option].} + +const + MAX_MYSQL_MANAGER_ERR* = 256 + MAX_MYSQL_MANAGER_MSG* = 256 + MANAGER_OK* = 200 + MANAGER_INFO* = 250 + MANAGER_ACCESS* = 401 + MANAGER_CLIENT_ERR* = 450 + MANAGER_INTERNAL_ERR* = 500 + +type + St_dynamic_array*{.final.} = object + buffer*: cstring + elements*: cuint + max_element*: cuint + alloc_increment*: cuint + size_of_element*: cuint + + DYNAMIC_ARRAY* = St_dynamic_array + Pst_dynamic_array* = ptr St_dynamic_array + Pst_mysql_options* = ptr St_mysql_options + St_mysql_options*{.final.} = object + connect_timeout*: cuint + read_timeout*: cuint + write_timeout*: cuint + port*: cuint + protocol*: cuint + client_flag*: int + host*: cstring + user*: cstring + password*: cstring + unix_socket*: cstring + db*: cstring + init_commands*: Pst_dynamic_array + my_cnf_file*: cstring + my_cnf_group*: cstring + charset_dir*: cstring + charset_name*: cstring + ssl_key*: cstring # PEM key file + ssl_cert*: cstring # PEM cert file + ssl_ca*: cstring # PEM CA file + ssl_capath*: cstring # PEM directory of CA-s? + ssl_cipher*: cstring # cipher to use + shared_memory_base_name*: cstring + max_allowed_packet*: int + use_ssl*: my_bool # if to use SSL or not + compress*: my_bool + named_pipe*: my_bool # On connect, find out the replication role of the server, and + # establish connections to all the peers + rpl_probe*: my_bool # Each call to mysql_real_query() will parse it to tell if it is a read + # or a write, and direct it to the slave or the master + rpl_parse*: my_bool # If set, never read from a master, only from slave, when doing + # a read that is replication-aware + no_master_reads*: my_bool + separate_thread*: my_bool + methods_to_use*: Option + client_ip*: cstring + secure_auth*: my_bool # Refuse client connecting to server if it uses old (pre-4.1.1) protocol + report_data_truncation*: my_bool # 0 - never report, 1 - always report (default) + # function pointers for local infile support + local_infile_init*: proc (para1: var pointer, para2: cstring, para3: pointer): cint{. + cdecl.} + local_infile_read*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_end*: proc (para1: pointer) + local_infile_error*: proc (para1: pointer, para2: cstring, para3: cuint): cint + local_infile_userdata*: pointer + + Status* = enum + STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT + Protocol_type* = enum # There are three types of queries - the ones that have to go to + # the master, the ones that go to a slave, and the administrative + # type which must happen on the pivot connection + PROTOCOL_DEFAULT, PROTOCOL_TCP, PROTOCOL_SOCKET, PROTOCOL_PIPE, + PROTOCOL_MEMORY + Rpl_type* = enum + RPL_MASTER, RPL_SLAVE, RPL_ADMIN + Charset_info_st*{.final.} = object + number*: cuint + primary_number*: cuint + binary_number*: cuint + state*: cuint + csname*: cstring + name*: cstring + comment*: cstring + tailoring*: cstring + ftype*: cstring + to_lower*: cstring + to_upper*: cstring + sort_order*: cstring + contractions*: ptr int16 + sort_order_big*: ptr ptr int16 + tab_to_uni*: ptr int16 + tab_from_uni*: pointer # was ^MY_UNI_IDX + state_map*: cstring + ident_map*: cstring + strxfrm_multiply*: cuint + mbminlen*: cuint + mbmaxlen*: cuint + min_sort_char*: int16 + max_sort_char*: int16 + escape_with_backslash_is_dangerous*: my_bool + cset*: pointer # was ^MY_CHARSET_HANDLER + coll*: pointer # was ^MY_COLLATION_HANDLER; + + CHARSET_INFO* = Charset_info_st + Pcharset_info_st* = ptr Charset_info_st + Pcharacter_set* = ptr Character_set + Character_set*{.final.} = object + number*: cuint + state*: cuint + csname*: cstring + name*: cstring + comment*: cstring + dir*: cstring + mbminlen*: cuint + mbmaxlen*: cuint + + MY_CHARSET_INFO* = Character_set + PMY_CHARSET_INFO* = ptr MY_CHARSET_INFO + Pst_mysql_methods* = ptr St_mysql_methods + Pst_mysql* = ptr St_mysql + St_mysql*{.final.} = object + net*: NET # Communication parameters + connector_fd*: gptr # ConnectorFd for SSL + host*: cstring + user*: cstring + passwd*: cstring + unix_socket*: cstring + server_version*: cstring + host_info*: cstring + info*: cstring + db*: cstring + charset*: Pcharset_info_st + fields*: PFIELD + field_alloc*: MEM_ROOT + affected_rows*: my_ulonglong + insert_id*: my_ulonglong # id if insert on table with NEXTNR + extra_info*: my_ulonglong # Used by mysqlshow, not used by mysql 5.0 and up + thread_id*: int # Id for connection in server + packet_length*: int + port*: cuint + client_flag*: int + server_capabilities*: int + protocol_version*: cuint + field_count*: cuint + server_status*: cuint + server_language*: cuint + warning_count*: cuint + options*: St_mysql_options + status*: Status + free_me*: my_bool # If free in mysql_close + reconnect*: my_bool # set to 1 if automatic reconnect + scramble*: array[0..(SCRAMBLE_LENGTH + 1) - 1, char] # session-wide random string + # Set if this is the original connection, not a master or a slave we have + # added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave() + rpl_pivot*: my_bool # Pointers to the master, and the next slave connections, points to + # itself if lone connection. + master*: Pst_mysql + next_slave*: Pst_mysql + last_used_slave*: Pst_mysql # needed for round-robin slave pick + last_used_con*: Pst_mysql # needed for send/read/store/use result to work correctly with replication + stmts*: pointer # was PList, list of all statements + methods*: Pst_mysql_methods + thd*: pointer # Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag + # from mysql_stmt_close if close had to cancel result set of this object. + unbuffered_fetch_owner*: Pmy_bool + + # MySQL* = St_mysql + PMySQL* = ptr St_mysql + Pst_mysql_res* = ptr St_mysql_res + St_mysql_res*{.final.} = object + row_count*: my_ulonglong + fields*: PFIELD + data*: PDATA + data_cursor*: PROWS + lengths*: ptr int # column lengths of current row + handle*: PMySQL # for unbuffered reads + field_alloc*: MEM_ROOT + field_count*: cuint + current_field*: cuint + row*: ROW # If unbuffered read + current_row*: ROW # buffer to current row + eof*: my_bool # Used by mysql_fetch_row + unbuffered_fetch_cancelled*: my_bool # mysql_stmt_close() had to cancel this result + methods*: Pst_mysql_methods + + RES* = St_mysql_res + PRES* = ptr RES + Pst_mysql_stmt* = ptr St_mysql_stmt + PSTMT* = ptr STMT + St_mysql_methods*{.final.} = object + read_query_result*: proc (MySQL: PMySQL): my_bool{.cdecl.} + advanced_command*: proc (MySQL: PMySQL, command: Enum_server_command, header: cstring, + header_length: int, arg: cstring, arg_length: int, + skip_check: my_bool): my_bool + read_rows*: proc (MySQL: PMySQL, fields: PFIELD, fields_count: cuint): PDATA + use_result*: proc (MySQL: PMySQL): PRES + fetch_lengths*: proc (fto: ptr int, column: ROW, field_count: cuint) + flush_use_result*: proc (MySQL: PMySQL) + list_fields*: proc (MySQL: PMySQL): PFIELD + read_prepare_result*: proc (MySQL: PMySQL, stmt: PSTMT): my_bool + stmt_execute*: proc (stmt: PSTMT): cint + read_binary_rows*: proc (stmt: PSTMT): cint + unbuffered_fetch*: proc (MySQL: PMySQL, row: cstringArray): cint + free_embedded_thd*: proc (MySQL: PMySQL) + read_statistics*: proc (MySQL: PMySQL): cstring + next_result*: proc (MySQL: PMySQL): my_bool + read_change_user_result*: proc (MySQL: PMySQL, buff: cstring, passwd: cstring): cint + read_rowsfrom_cursor*: proc (stmt: PSTMT): cint + + METHODS* = St_mysql_methods + PMETHODS* = ptr METHODS + Pst_mysql_manager* = ptr St_mysql_manager + St_mysql_manager*{.final.} = object + net*: NET + host*: cstring + user*: cstring + passwd*: cstring + port*: cuint + free_me*: my_bool + eof*: my_bool + cmd_status*: cint + last_errno*: cint + net_buf*: cstring + net_buf_pos*: cstring + net_data_end*: cstring + net_buf_size*: cint + last_error*: array[0..(MAX_MYSQL_MANAGER_ERR) - 1, char] + + MANAGER* = St_mysql_manager + PMANAGER* = ptr MANAGER + Pst_mysql_parameters* = ptr St_mysql_parameters + St_mysql_parameters*{.final.} = object + p_max_allowed_packet*: ptr int + p_net_buffer_length*: ptr int + + PARAMETERS* = St_mysql_parameters + PPARAMETERS* = ptr PARAMETERS + Enum_mysql_stmt_state* = enum + STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE + Pst_mysql_bind* = ptr St_mysql_bind + # St_mysql_bind*{.final.} = object + # len*: int # output length pointer + # is_null*: Pmy_bool # Pointer to null indicator + # buffer*: pointer # buffer to get/put data + # error*: Pmy_bool # set this if you want to track data truncations happened during fetch + # buffer_type*: Enum_field_types # buffer type + # buffer_length*: int # buffer length, must be set for str/binary + # # Following are for internal use. Set by mysql_stmt_bind_param + # row_ptr*: ptr byte # for the current data position + # offset*: int # offset position for char/binary fetch + # length_value*: int # Used if length is 0 + # param_number*: cuint # For null count and error messages + # pack_length*: cuint # Internal length for packed data + # error_value*: my_bool # used if error is 0 + # is_unsigned*: my_bool # set if integer type is unsigned + # long_data_used*: my_bool # If used with mysql_send_long_data + # is_null_value*: my_bool # Used if is_null is 0 + # store_param_func*: proc (net: PNET, param: Pst_mysql_bind){.cdecl.} + # fetch_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) + # skip_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte) + + U* {.final, union, pure.} = object # union for row_ptr and indicator + row_ptr*: ptr byte # for the current data position + indicator*: cstring + + St_mysql_bind*{.final.} = object + length*: ptr culong # output length pointer + is_null*: Pmy_bool # Pointer to null indicator + buffer*: pointer # buffer to get/put data + error*: Pmy_bool # set this if you want to track data truncations happened during fetch + u*: U + store_param_func*: proc (net: PNET, param: ptr St_mysql_bind){.cdecl.} + fetch_result*: proc (param: ptr St_mysql_bind, field: PFIELD, row: PPByte){.cdecl.} + skip_result*: proc (param: ptr St_mysql_bind, field: PFIELD, row: PPByte){.cdecl.} + buffer_length*: culong # buffer length, must be set when fetching str/binary + offset*: culong # offset position for char/binary fetch + length_value*: culong # Used if length is 0 + flags*: cuint # special flags, e.g. for dummy bind + pack_length*: cuint # Internal length for packed data + buffer_type*: Enum_field_types # buffer type + error_value*: my_bool # used if error is 0 + is_unsigned*: my_bool # set if integer type is unsigned + long_data_used*: my_bool # If used with mysql_send_long_data + is_null_value*: my_bool # Used if is_null is 0 + extension*: pointer + + BIND* = St_mysql_bind + PBIND* = ptr BIND # statement handler + St_mysql_stmt*{.final.} = object + mem_root*: MEM_ROOT # root allocations + mysql*: PMySQL # connection handle + params*: PBIND # input parameters + `bind`*: PBIND # input parameters + fields*: PFIELD # result set metadata + result*: DATA # cached result set + data_cursor*: PROWS # current row in cached result + affected_rows*: my_ulonglong # copy of mysql->affected_rows after statement execution + insert_id*: my_ulonglong + read_row_func*: proc (stmt: Pst_mysql_stmt, row: PPByte): cint{.cdecl.} + stmt_id*: int # Id for prepared statement + flags*: int # i.e. type of cursor to open + prefetch_rows*: int # number of rows per one COM_FETCH + server_status*: cuint # Copied from mysql->server_status after execute/fetch to know + # server-side cursor status for this statement. + last_errno*: cuint # error code + param_count*: cuint # input parameter count + field_count*: cuint # number of columns in result set + state*: Enum_mysql_stmt_state # statement state + last_error*: array[0..(ERRMSG_SIZE) - 1, char] # error message + sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char] + send_types_to_server*: my_bool # Types of input parameters should be sent to server + bind_param_done*: my_bool # input buffers were supplied + bind_result_done*: char # output buffers were supplied + unbuffered_fetch_cancelled*: my_bool + update_max_length*: my_bool + + STMT* = St_mysql_stmt + + Enum_stmt_attr_type* = enum + STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS +{.deprecated: [Tst_dynamic_array: St_dynamic_array, Tst_mysql_options: St_mysql_options, + TDYNAMIC_ARRAY: DYNAMIC_ARRAY, Tprotocol_type: Protocol_type, + Trpl_type: Rpl_type, Tcharset_info_st: Charset_info_st, + TCHARSET_INFO: CHARSET_INFO, Tcharacter_set: Character_set, + TMY_CHARSET_INFO: MY_CHARSET_INFO, Tst_mysql: St_mysql, + Tst_mysql_methods: St_mysql_methods, TMySql: St_mysql, + Tst_mysql_res: St_mysql_res, TMETHODS: METHODS, TRES: RES, + Tst_mysql_manager: St_mysql_manager, TMANAGER: MANAGER, + Tst_mysql_parameters: St_mysql_parameters, TPARAMETERS: PARAMETERS, + Tenum_mysql_stmt_state: Enum_mysql_stmt_state, + Tst_mysql_bind: St_mysql_bind, TBIND: BIND, Tst_mysql_stmt: St_mysql_stmt, + TSTMT: STMT, Tenum_stmt_attr_type: Enum_stmt_attr_type, + Tstatus: Status].} + +proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. + cdecl, dynlib: lib, importc: "mysql_server_init".} +proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} + # mysql_server_init/end need to be called when using libmysqld or + # libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so + # you don't need to call it explicitly; but you need to call + # mysql_server_end() to free memory). The names are a bit misleading + # (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general + # names which suit well whether you're using libmysqld or libmysqlclient. We + # intend to promote these aliases over the mysql_server* ones. +proc library_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{. + cdecl, dynlib: lib, importc: "mysql_server_init".} +proc library_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".} +proc get_parameters*(): PPARAMETERS{.stdcall, dynlib: lib, + importc: "mysql_get_parameters".} + # Set up and bring down a thread; these function should be called + # for each thread in an application which opens at least one MySQL + # connection. All uses of the connection(s) should be between these + # function calls. +proc thread_init*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_thread_init".} +proc thread_end*(){.stdcall, dynlib: lib, importc: "mysql_thread_end".} + # Functions to get information from the MYSQL and MYSQL_RES structures + # Should definitely be used if one uses shared libraries. +proc num_rows*(res: PRES): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_num_rows".} +proc num_fields*(res: PRES): cuint{.stdcall, dynlib: lib, + importc: "mysql_num_fields".} +proc eof*(res: PRES): my_bool{.stdcall, dynlib: lib, importc: "mysql_eof".} +proc fetch_field_direct*(res: PRES, fieldnr: cuint): PFIELD{.stdcall, + dynlib: lib, importc: "mysql_fetch_field_direct".} +proc fetch_fields*(res: PRES): PFIELD{.stdcall, dynlib: lib, + importc: "mysql_fetch_fields".} +proc row_tell*(res: PRES): ROW_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_row_tell".} +proc field_tell*(res: PRES): FIELD_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_field_tell".} +proc field_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_field_count".} +proc affected_rows*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_affected_rows".} +proc insert_id*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_insert_id".} +proc errno*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, importc: "mysql_errno".} +proc error*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_error".} +proc sqlstate*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_sqlstate".} +proc warning_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_warning_count".} +proc info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_info".} +proc thread_id*(MySQL: PMySQL): int{.stdcall, dynlib: lib, importc: "mysql_thread_id".} +proc character_set_name*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_character_set_name".} +proc set_character_set*(MySQL: PMySQL, csname: cstring): int32{.stdcall, dynlib: lib, + importc: "mysql_set_character_set".} +proc init*(MySQL: PMySQL): PMySQL{.stdcall, dynlib: lib, importc: "mysql_init".} +# proc init*(MySQL: var PMySQL){.stdcall, dynlib: lib, importc: "mysql_init".} +proc ssl_set*(MySQL: PMySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring, + cipher: cstring): my_bool{.stdcall, dynlib: lib, + importc: "mysql_ssl_set".} +proc change_user*(MySQL: PMySQL, user: cstring, passwd: cstring, db: cstring): my_bool{. + stdcall, dynlib: lib, importc: "mysql_change_user".} +proc real_connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: int): PMySQL{.stdcall, dynlib: lib, + importc: "mysql_real_connect".} +proc real_connect_start*(ret: ptr PMySQL, mysql: PMySQL, host: cstring, user: cstring, passwd: cstring, + db: cstring, port: cuint, unix_socket: cstring, + clientflag: culong): cint{.stdcall, dynlib: lib, + importc: "mysql_real_connect_start".} +proc fetch_row_nonblocking*(MYSQL_RES: PRES, MYSQL_ROW: PROW): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_fetch_row_nonblocking".} +proc free_result_nonblocking*(MYSQL_RES: PRES): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_free_result_nonblocking".} +proc fetch_next_result_nonblocking*(MySQL: PMySQL): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_next_result_nonblocking".} +proc real_query_nonblocking*(MySQL: PMySQL, q: cstring, length: int): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_real_query_nonblocking".} +proc store_result_nonblocking*(MySQL: PMySQL, MYSQL_RES: ptr PRES): NetAsyncStatus{.stdcall, dynlib: lib, + importc: "mysql_store_result_nonblocking".} +proc select_db*(MySQL: PMySQL, db: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_select_db".} +proc query*(MySQL: PMySQL, q: cstring): cint{.stdcall, dynlib: lib, importc: "mysql_query".} +proc send_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_send_query".} +proc real_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib, + importc: "mysql_real_query".} +proc store_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, + importc: "mysql_store_result".} +proc use_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, importc: "mysql_use_result".} + # perform query on master +proc master_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_master_query".} +proc master_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, + dynlib: lib, importc: "mysql_master_send_query".} + # perform query on slave +proc slave_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_slave_query".} +proc slave_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, + dynlib: lib, importc: "mysql_slave_send_query".} +proc get_character_set_info*(MySQL: PMySQL, charset: PMY_CHARSET_INFO){.stdcall, + dynlib: lib, importc: "mysql_get_character_set_info".} + # local infile support +const + LOCAL_INFILE_ERROR_LEN* = 512 + +# procedure mysql_set_local_infile_handler(mysql:PMYSQL; local_infile_init:function (para1:Ppointer; para2:Pchar; para3:pointer):longint; local_infile_read:function (para1:pointer; para2:Pchar; para3:dword):longint; local_infile_end:procedure (_pa +# para6:pointer);cdecl;external mysqllib name 'mysql_set_local_infile_handler'; + +proc set_local_infile_default*(MySQL: PMySQL){.cdecl, dynlib: lib, + importc: "mysql_set_local_infile_default".} + # enable/disable parsing of all queries to decide if they go on master or + # slave +proc enable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_enable_rpl_parse".} +proc disable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_disable_rpl_parse".} + # get the value of the parse flag +proc rpl_parse_enabled*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_rpl_parse_enabled".} + # enable/disable reads from master +proc enable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, + importc: "mysql_enable_reads_from_master".} +proc disable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, importc: "mysql_disable_reads_from_master".} + # get the value of the master read flag +proc reads_from_master_enabled*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_reads_from_master_enabled".} +proc rpl_query_type*(q: cstring, length: cint): Rpl_type{.stdcall, dynlib: lib, + importc: "mysql_rpl_query_type".} + # discover the master and its slaves +proc rpl_probe*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rpl_probe".} + # set the master, close/free the old one, if it is not a pivot +proc set_master*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. + stdcall, dynlib: lib, importc: "mysql_set_master".} +proc add_slave*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{. + stdcall, dynlib: lib, importc: "mysql_add_slave".} +proc shutdown*(MySQL: PMySQL, shutdown_level: Enum_shutdown_level): cint{.stdcall, + dynlib: lib, importc: "mysql_shutdown".} +proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, + importc: "mysql_dump_debug_info".} +proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib, + importc: "mysql_refresh".} +proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".} +proc set_server_option*(MySQL: PMySQL, option: Enum_mysql_set_option): cint{.stdcall, + dynlib: lib, importc: "mysql_set_server_option".} +proc ping*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_ping".} +proc stat*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_stat".} +proc get_server_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_server_info".} +proc get_client_info*(): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_client_info".} +proc get_client_version*(): int{.stdcall, dynlib: lib, + importc: "mysql_get_client_version".} +proc get_host_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, + importc: "mysql_get_host_info".} +proc get_server_version*(MySQL: PMySQL): int{.stdcall, dynlib: lib, + importc: "mysql_get_server_version".} +proc get_proto_info*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, + importc: "mysql_get_proto_info".} +proc list_dbs*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_dbs".} +proc list_tables*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_tables".} +proc list_processes*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, + importc: "mysql_list_processes".} +proc options*(MySQL: PMySQL, option: Option, arg: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_options".} +proc free_result*(result: PRES){.stdcall, dynlib: lib, + importc: "mysql_free_result".} +proc data_seek*(result: PRES, offset: my_ulonglong){.stdcall, dynlib: lib, + importc: "mysql_data_seek".} +proc row_seek*(result: PRES, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_row_seek".} +proc field_seek*(result: PRES, offset: FIELD_OFFSET): FIELD_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_field_seek".} +proc fetch_row*(result: PRES): ROW{.stdcall, dynlib: lib, + importc: "mysql_fetch_row".} +proc fetch_lengths*(result: PRES): ptr int{.stdcall, dynlib: lib, + importc: "mysql_fetch_lengths".} +proc fetch_field*(result: PRES): PFIELD{.stdcall, dynlib: lib, + importc: "mysql_fetch_field".} +proc list_fields*(MySQL: PMySQL, table: cstring, wild: cstring): PRES{.stdcall, + dynlib: lib, importc: "mysql_list_fields".} +proc escape_string*(fto: cstring, `from`: cstring, from_length: int): int{. + stdcall, dynlib: lib, importc: "mysql_escape_string".} +proc hex_string*(fto: cstring, `from`: cstring, from_length: int): int{.stdcall, + dynlib: lib, importc: "mysql_hex_string".} +proc real_escape_string*(MySQL: PMySQL, fto: cstring, `from`: cstring, len: int): int{. + stdcall, dynlib: lib, importc: "mysql_real_escape_string".} +proc debug*(debug: cstring){.stdcall, dynlib: lib, importc: "mysql_debug".} + # function mysql_odbc_escape_string(mysql:PMYSQL; fto:Pchar; to_length:dword; from:Pchar; from_length:dword; + # param:pointer; extend_buffer:function (para1:pointer; to:Pchar; length:Pdword):Pchar):Pchar;stdcall;external mysqllib name 'mysql_odbc_escape_string'; +proc myodbc_remove_escape*(MySQL: PMySQL, name: cstring){.stdcall, dynlib: lib, + importc: "myodbc_remove_escape".} +proc thread_safe*(): cuint{.stdcall, dynlib: lib, importc: "mysql_thread_safe".} +proc embedded*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_embedded".} +proc manager_init*(con: PMANAGER): PMANAGER{.stdcall, dynlib: lib, + importc: "mysql_manager_init".} +proc manager_connect*(con: PMANAGER, host: cstring, user: cstring, + passwd: cstring, port: cuint): PMANAGER{.stdcall, + dynlib: lib, importc: "mysql_manager_connect".} +proc manager_close*(con: PMANAGER){.stdcall, dynlib: lib, + importc: "mysql_manager_close".} +proc manager_command*(con: PMANAGER, cmd: cstring, cmd_len: cint): cint{. + stdcall, dynlib: lib, importc: "mysql_manager_command".} +proc manager_fetch_line*(con: PMANAGER, res_buf: cstring, res_buf_size: cint): cint{. + stdcall, dynlib: lib, importc: "mysql_manager_fetch_line".} +proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_read_query_result".} +proc stmt_init*(MySQL: PMySQL): PSTMT{.stdcall, dynlib: lib, importc: "mysql_stmt_init".} +proc stmt_prepare*(stmt: PSTMT, query: cstring, len: int): cint{.stdcall, + dynlib: lib, importc: "mysql_stmt_prepare".} +proc stmt_execute*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_execute".} +proc stmt_fetch*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_fetch".} +proc stmt_fetch_column*(stmt: PSTMT, `bind`: PBIND, column: cuint, offset: int): cint{. + stdcall, dynlib: lib, importc: "mysql_stmt_fetch_column".} +proc stmt_store_result*(stmt: PSTMT): cint{.stdcall, dynlib: lib, + importc: "mysql_stmt_store_result".} +proc stmt_param_count*(stmt: PSTMT): int{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_count".} +proc stmt_attr_set*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_set".} +proc stmt_attr_get*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{. + stdcall, dynlib: lib, importc: "mysql_stmt_attr_get".} +proc stmt_bind_param*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_param".} +proc stmt_bind_result*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_bind_result".} +proc stmt_close*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_close".} +proc stmt_reset*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_reset".} +proc stmt_free_result*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_free_result".} +proc stmt_send_long_data*(stmt: PSTMT, param_number: cuint, data: cstring, + len: int): my_bool{.stdcall, dynlib: lib, + importc: "mysql_stmt_send_long_data".} +proc stmt_result_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_result_metadata".} +proc stmt_param_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib, + importc: "mysql_stmt_param_metadata".} +proc stmt_errno*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, + importc: "mysql_stmt_errno".} +proc stmt_error*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_error".} +proc stmt_sqlstate*(stmt: PSTMT): cstring{.stdcall, dynlib: lib, + importc: "mysql_stmt_sqlstate".} +proc stmt_row_seek*(stmt: PSTMT, offset: ROW_OFFSET): ROW_OFFSET{.stdcall, + dynlib: lib, importc: "mysql_stmt_row_seek".} +proc stmt_row_tell*(stmt: PSTMT): ROW_OFFSET{.stdcall, dynlib: lib, + importc: "mysql_stmt_row_tell".} +proc stmt_data_seek*(stmt: PSTMT, offset: my_ulonglong){.stdcall, dynlib: lib, + importc: "mysql_stmt_data_seek".} +proc stmt_num_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_num_rows".} +proc stmt_affected_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_affected_rows".} +proc stmt_insert_id*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib, + importc: "mysql_stmt_insert_id".} +proc stmt_field_count*(stmt: PSTMT): cuint{.stdcall, dynlib: lib, + importc: "mysql_stmt_field_count".} +proc commit*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_commit".} +proc rollback*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rollback".} +proc autocommit*(MySQL: PMySQL, auto_mode: my_bool): my_bool{.stdcall, dynlib: lib, + importc: "mysql_autocommit".} +proc more_results*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, + importc: "mysql_more_results".} +proc next_result*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_next_result".} +proc close*(sock: PMySQL){.stdcall, dynlib: lib, importc: "mysql_close".} + # status return codes +const + NO_DATA* = 100 + DATA_TRUNCATED* = 101 + +proc reload*(x: PMySQL): cint +when defined(USE_OLD_FUNCTIONS): + proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall, + dynlib: lib, importc: "mysql_connect".} + proc create_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_create_db".} + proc drop_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib, + importc: "mysql_drop_db".} +proc net_safe_read*(MySQL: PMySQL): cuint{.cdecl, dynlib: lib, importc: "net_safe_read".} + +proc IS_PRI_KEY(n: int32): bool = + result = (n and PRI_KEY_FLAG) != 0 + +proc IS_NOT_NULL(n: int32): bool = + result = (n and NOT_NULL_FLAG) != 0 + +proc IS_BLOB(n: int32): bool = + result = (n and BLOB_FLAG) != 0 + +proc IS_NUM_FIELD(f: Pst_mysql_field): bool = + result = (f.flags and NUM_FLAG) != 0 + +proc IS_NUM(t: Enum_field_types): bool = + result = (t <= FIELD_TYPE_INT24) or (t == FIELD_TYPE_YEAR) or + (t == FIELD_TYPE_NEWDECIMAL) + +proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool = + result = (f.ftype <= FIELD_TYPE_INT24) and + ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or + (f.ftype == FIELD_TYPE_YEAR) + +proc reload(x: PMySQL): cint = + result = refresh(x, REFRESH_GRANT) + +{.pop.} +when defined(nimHasStyleChecks): + {.pop.} diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_impl.nim b/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_impl.nim new file mode 100644 index 0000000..bc20c12 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_impl.nim @@ -0,0 +1,431 @@ +## https://www.postgresql.jp/document/12/html/libpq-async.html + +import std/asyncdispatch +import std/json +import std/monotimes +import std/strutils +import std/times +import ../../error +import ../../models/database_types +import ../../prepared_param +import ./postgres_rdb +import ./postgres_lib + + +type + PgWaitState = object + cancelled: bool + +proc cancelQuery(db: PPGconn) {.raises: [DbError].} = + let cancel = pqGetCancel(db) + if cancel == nil: + raise newException(DbError, "PQgetCancel failed") + defer: + pqFreeCancel(cancel) + var errBuf = newStringOfCap(ERROR_MSG_LENGTH) + errBuf.setLen(ERROR_MSG_LENGTH) + if pqCancel(cancel, errBuf.cstring, int32(errBuf.len)) == 0: + raise newException(DbError, "PQcancel failed: " & $errBuf.cstring) + +proc ensurePgSocketRegistered(db: PPGconn): AsyncFD = + let sock = pqsocket(db) + if sock < 0: + dbError(db) + result = AsyncFD(cint(sock)) + let disp = getGlobalDispatcher() + if not disp.contains(result): + register(result) + +proc waitPgIo(db: PPGconn, timeoutMs: int; forRead: bool): Future[bool] {.async.} = + if timeoutMs <= 0: + return false + let fd = ensurePgSocketRegistered(db) + var state = PgWaitState(cancelled: false) + var ioFut = newFuture[void]("waitPgIo") + if forRead: + proc readCb(f: AsyncFD): bool = + if state.cancelled: + return true + if not ioFut.finished: + ioFut.complete() + return true + addRead(fd, readCb) + else: + proc writeCb(f: AsyncFD): bool = + if state.cancelled: + return true + if not ioFut.finished: + ioFut.complete() + return true + addWrite(fd, writeCb) + let ok = await withTimeout(ioFut, timeoutMs) + if not ok: + state.cancelled = true + unregister(fd) + return ok + +proc makePgDeadline(timeout: int): MonoTime = + let sec = if timeout > 0: timeout else: 0 + getMonoTime() + initDuration(seconds = sec) + +proc pgRemainingMs(deadline: MonoTime): int = + let left = (deadline - getMonoTime()).inMilliseconds + if left <= 0: + return 0 + if left > int64(high(int)): + return high(int) + result = int(left) + if result < 1: + result = 1 + +proc pgSendQueryParams(db: PPGconn, query: string, pgParams: PGParams) {.raises: [DbError].} = + let status = + if pgParams.nParams > 0: + pqsendQueryParams(db, query.cstring, pgParams.nParams, nil, pgParams.values, pgParams.lengths[0].unsafeAddr, pgParams.formats[0].unsafeAddr, 0) + else: + pqsendQueryParams(db, query.cstring, pgParams.nParams, nil, nil, nil, nil, 0) + if status != 1: + dbError(db) + +proc pgFlushOutgoing(db: PPGconn, deadline: MonoTime): Future[void] {.async.} = + while true: + let flushRes = pqflush(db) + if flushRes == 0: + return + if flushRes < 0: + dbError(db) + let ms = pgRemainingMs(deadline) + if ms <= 0: + cancelQuery(db) + raise newException(DbError, "PostgreSQL query timeout") + if not await waitPgIo(db, ms, false): + cancelQuery(db) + raise newException(DbError, "PostgreSQL query timeout") + +proc pgAwaitReadyForGetResult(db: PPGconn, deadline: MonoTime): Future[void] {.async.} = + while true: + if pqconsumeInput(db) != 1: + dbError(db) + if pqisBusy(db) != 1: + return + let ms = pgRemainingMs(deadline) + if ms <= 0: + cancelQuery(db) + raise newException(DbError, "PostgreSQL query timeout") + if not await waitPgIo(db, ms, true): + cancelQuery(db) + raise newException(DbError, "PostgreSQL query timeout") + +proc pgNextResult(db: PPGconn, deadline: MonoTime): Future[PPGresult] {.async.} = + await pgAwaitReadyForGetResult(db, deadline) + result = pqgetResult(db) + +proc pgEnsureIdle(db: PPGconn, deadline: MonoTime): Future[void] {.async.} = + while true: + await pgAwaitReadyForGetResult(db, deadline) + let r = pqgetResult(db) + if r == nil: + db.checkError() + return + pqclear(r) + + +proc query*(db: PPGconn, query: string, args: JsonNode, timeout: int): Future[(seq[Row], DbRows)] {.async.} = + assert db.status == CONNECTION_OK + let pgParams = PGParams.fromObjArray(args) + defer: + if pgParams.nParams > 0: pgParams.values.deallocCStringArray() + pgSendQueryParams(db, query, pgParams) + var dbRows: DbRows + var rows = newSeq[Row]() + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + + let cols = pqnfields(pqresult) + var row = newRow(cols) + let base = buildBaseDbColumns(pqresult, cols) + for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + appendDbRowWithBaseColumns(pqresult, dbRows, i, cols, base) + rows.add(row) + pqclear(pqresult) + + return (rows, dbRows) + + +proc exec*(db: PPGconn, query: string, args: JsonNode, columns: seq[Row], timeout: int) {.async.} = + assert db.status == CONNECTION_OK + let pgParams = PGParams.fromObjArray(args, columns) + defer: + if pgParams.nParams > 0: pgParams.values.deallocCStringArray() + pgSendQueryParams(db, query, pgParams) + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + pqclear(pqresult) + + +proc execGetValue*(db: PPGconn, query: string, args: JsonNode, columns: seq[Row], timeout: int): Future[(seq[Row], DbRows)] {.async.} = + assert db.status == CONNECTION_OK + let pgParams = PGParams.fromObjArray(args, columns) + defer: + if pgParams.nParams > 0: pgParams.values.deallocCStringArray() + pgSendQueryParams(db, query, pgParams) + var dbRows: DbRows + var rows = newSeq[Row]() + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + + let cols = pqnfields(pqresult) + var row = newRow(cols) + let base = buildBaseDbColumns(pqresult, cols) + for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + appendDbRowWithBaseColumns(pqresult, dbRows, i, cols, base) + rows.add(row) + pqclear(pqresult) + + return (rows, dbRows) + + +proc rawQuery*(db: PPGconn, query: string, args: JsonNode, timeout: int): Future[(seq[Row], DbRows)] {.async.} = + assert db.status == CONNECTION_OK + let pgParams = PGParams.fromArray(args) + defer: + if pgParams.nParams > 0: pgParams.values.deallocCStringArray() + pgSendQueryParams(db, query, pgParams) + var dbRows: DbRows + var rows = newSeq[Row]() + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + + let cols = pqnfields(pqresult) + var row = newRow(cols) + let base = buildBaseDbColumns(pqresult, cols) + for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + appendDbRowWithBaseColumns(pqresult, dbRows, i, cols, base) + rows.add(row) + pqclear(pqresult) + + return (rows, dbRows) + + +proc rawExec*(db: PPGconn, query: string, args: JsonNode, timeout: int) {.async.} = + assert db.status == CONNECTION_OK + let pgParams = PGParams.fromArray(args) + defer: + if pgParams.nParams > 0: pgParams.values.deallocCStringArray() + pgSendQueryParams(db, query, pgParams) + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + pqclear(pqresult) + + +# ================================================== +# Old functions +# ================================================== + +proc query*(db: PPGconn, query: string, args: seq[string], timeout: int): Future[(seq[Row], DbRows)] {.async.} = + assert db.status == CONNECTION_OK + let status = pqsendQuery(db, dbFormat(query, args).cstring) + if status != 1: dbError(db) + var dbRows: DbRows + var rows = newSeq[Row]() + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + + let cols = pqnfields(pqresult) + var row = newRow(cols) + let base = buildBaseDbColumns(pqresult, cols) + for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + appendDbRowWithBaseColumns(pqresult, dbRows, i, cols, base) + rows.add(row) + pqclear(pqresult) + + return (rows, dbRows) + +proc queryPlain*(db: PPGconn, query: string, args: seq[string], timeout: int): Future[seq[Row]] {.async.} = + assert db.status == CONNECTION_OK + let status = pqsendQuery(db, dbFormat(query, args).cstring) + if status != 1: dbError(db) + var rows = newSeq[Row]() + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + + var cols = pqnfields(pqresult) + var row = newRow(cols) + for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + rows.add(row) + pqclear(pqresult) + + return rows + + +proc exec*(db: PPGconn, query: string, args: seq[string], timeout: int) {.async.} = + assert db.status == CONNECTION_OK + let success = pqsendQuery(db, dbFormat(query, args).cstring) + if success != 1: dbError(db) + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + pqclear(pqresult) + + +proc getColumns*(db: PPGconn, query: string, args: seq[string], timeout: int): Future[seq[string]] {.async.} = + assert db.status == CONNECTION_OK + let status = pqsendQuery(db, dbFormat(query, args).cstring) + if status != 1: dbError(db) + var dbRows: DbRows + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + + let cols = pqnfields(pqresult) + let base = buildBaseDbColumns(pqresult, cols) + appendDbRowWithBaseColumns(pqresult, dbRows, 0, cols, base) + pqclear(pqresult) + + for column in dbRows[0]: + result.add(column.name) + + +proc prepare*(db: PPGconn, query: string, timeout: int, stmtName: string, nArgs: int): Future[void] {.async.} = + assert db.status == CONNECTION_OK + let success = pqsendPrepare(db, stmtName, questionToDaller(query).cstring, int32(nArgs), nil) + if success != 1: dbError(db) + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + pqclear(pqresult) + + +proc deallocate*(db: PPGconn, stmtName: string, timeout: int): Future[void] {.async.} = + assert db.status == CONNECTION_OK + if stmtName.len == 0: + return + let success = pqsendQuery(db, ("DEALLOCATE " & stmtName).cstring) + if success != 1: + dbError(db) + let deadline = makePgDeadline(timeout) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + pqclear(pqresult) + +proc allocPreparedCStringArray(args: seq[PreparedParam]): cstringArray = + result = cast[cstringArray](alloc0((args.len + 1) * sizeof(cstring))) + for i, arg in args: + if arg.isNull: + continue + let cstrLen = arg.value.len + 1 + let cstr = cast[cstring](alloc0(cstrLen)) + copyMem(cstr, arg.value.cstring, arg.value.len) + result[i] = cstr + + +proc freePreparedCStringArray(values: cstringArray, n: int) = + if values.isNil: + return + for i in 0 ..< n: + if values[i] != nil: + dealloc(values[i]) + dealloc(values) + + +proc preparedQuery*(db: PPGconn, args: seq[PreparedParam], nArgs: int, timeout: int, stmtName: string): Future[(seq[Row], DbRows)] {.async.} = + assert db.status == CONNECTION_OK + let deadline = makePgDeadline(timeout) + await pgEnsureIdle(db, deadline) + let values = allocPreparedCStringArray(args) + defer: + freePreparedCStringArray(values, args.len) + let status = pqsendQueryPrepared(db, stmtName, int32(nArgs), values, nil, nil, 0) + if status != 1: dbError(db) + var dbRows: DbRows + var rows = newSeq[Row]() + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + + let cols = pqnfields(pqresult) + var row = newRow(cols) + let base = buildBaseDbColumns(pqresult, cols) + for i in 0'i32 .. pqNtuples(pqresult) - 1: + setRow(pqresult, row, i, cols) + appendDbRowWithBaseColumns(pqresult, dbRows, i, cols, base) + rows.add(row) + pqclear(pqresult) + + return (rows, dbRows) + +proc preparedExec*(db: PPGconn, args: seq[PreparedParam], nArgs: int, timeout: int, stmtName: string) {.async.} = + assert db.status == CONNECTION_OK + let deadline = makePgDeadline(timeout) + await pgEnsureIdle(db, deadline) + let values = allocPreparedCStringArray(args) + defer: + freePreparedCStringArray(values, args.len) + let status = pqsendQueryPrepared(db, stmtName, int32(nArgs), values, nil, nil, 0) + if status != 1: dbError(db) + await pgFlushOutgoing(db, deadline) + while true: + let pqresult = await pgNextResult(db, deadline) + if pqresult == nil: + db.checkError() + break + pqclear(pqresult) diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_lib.nim b/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_lib.nim new file mode 100644 index 0000000..06b6a68 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_lib.nim @@ -0,0 +1,364 @@ +import std/strutils +import std/json +import ../../error +import ../../models/database_types +import ./postgres_rdb + + +proc dbError*(db: PPGconn) {.noreturn.} = + ## raises a DbError exception. + var e: ref DbError + new(e) + e.msg = $pqErrorMessage(db) + raise e + +proc checkError*(db: PPGconn) = + ## Raises a DbError exception. + var message = pqErrorMessage(db) + if message.len > 0: + raise newException(DbError, $message) + +proc getBaseColumnType(res: PPGresult, col: int32): DbType = + ## OID → DbType(行に依存しない)。NULL セルは行ごとに `pqgetisnull` で上書きする。 + let oid = pqftype(res, col) + case oid + of 16: return DbType(kind: DbTypeKind.dbBool, name: "bool") + of 17: return DbType(kind: DbTypeKind.dbBlob, name: "bytea") + + of 21: return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2) + of 23: return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4) + of 20: return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8) + of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit") + of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit") + + of 18: return DbType(kind: DbTypeKind.dbFixedChar, name: "char") + of 19: return DbType(kind: DbTypeKind.dbFixedChar, name: "name") + of 1042: return DbType(kind: DbTypeKind.dbFixedChar, name: "bpchar") + + of 25: return DbType(kind: DbTypeKind.dbVarchar, name: "text") + of 1043: return DbType(kind: DbTypeKind.dbVarChar, name: "varchar") + of 2275: return DbType(kind: DbTypeKind.dbVarchar, name: "cstring") + + of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4") + of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8") + + of 790: return DbType(kind: DbTypeKind.dbDecimal, name: "money") + of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric") + + of 704: return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval") + of 702: return DbType(kind: DbTypeKind.dbTimestamp, name: "abstime") + of 703: return DbType(kind: DbTypeKind.dbTimeInterval, name: "reltime") + of 1082: return DbType(kind: DbTypeKind.dbDate, name: "date") + of 1083: return DbType(kind: DbTypeKind.dbTime, name: "time") + of 1114: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamp") + of 1184: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamptz") + of 1186: return DbType(kind: DbTypeKind.dbTimeInterval, name: "interval") + of 1266: return DbType(kind: DbTypeKind.dbTime, name: "timetz") + + of 114: return DbType(kind: DbTypeKind.dbJson, name: "json") + of 142: return DbType(kind: DbTypeKind.dbXml, name: "xml") + of 3802: return DbType(kind: DbTypeKind.dbJson, name: "jsonb") + + of 600: return DbType(kind: DbTypeKind.dbPoint, name: "point") + of 601: return DbType(kind: DbTypeKind.dbLseg, name: "lseg") + of 602: return DbType(kind: DbTypeKind.dbPath, name: "path") + of 603: return DbType(kind: DbTypeKind.dbBox, name: "box") + of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon") + of 628: return DbType(kind: DbTypeKind.dbLine, name: "line") + of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle") + + of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr") + of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr") + of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet") + + of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid") + of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector") + of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery") + of 2970: return DbType(kind: DbTypeKind.dbVarchar, name: "txid_snapshot") + + of 27: return DbType(kind: DbTypeKind.dbComposite, name: "tid") + of 1790: return DbType(kind: DbTypeKind.dbComposite, name: "refcursor") + of 2249: return DbType(kind: DbTypeKind.dbComposite, name: "record") + of 3904: return DbType(kind: DbTypeKind.dbComposite, name: "int4range") + of 3906: return DbType(kind: DbTypeKind.dbComposite, name: "numrange") + of 3908: return DbType(kind: DbTypeKind.dbComposite, name: "tsrange") + of 3910: return DbType(kind: DbTypeKind.dbComposite, name: "tstzrange") + of 3912: return DbType(kind: DbTypeKind.dbComposite, name: "daterange") + of 3926: return DbType(kind: DbTypeKind.dbComposite, name: "int8range") + + of 22: return DbType(kind: DbTypeKind.dbArray, name: "int2vector") + of 30: return DbType(kind: DbTypeKind.dbArray, name: "oidvector") + of 143: return DbType(kind: DbTypeKind.dbArray, name: "xml[]") + of 199: return DbType(kind: DbTypeKind.dbArray, name: "json[]") + of 629: return DbType(kind: DbTypeKind.dbArray, name: "line[]") + of 651: return DbType(kind: DbTypeKind.dbArray, name: "cidr[]") + of 719: return DbType(kind: DbTypeKind.dbArray, name: "circle[]") + of 791: return DbType(kind: DbTypeKind.dbArray, name: "money[]") + of 1000: return DbType(kind: DbTypeKind.dbArray, name: "bool[]") + of 1001: return DbType(kind: DbTypeKind.dbArray, name: "bytea[]") + of 1002: return DbType(kind: DbTypeKind.dbArray, name: "char[]") + of 1003: return DbType(kind: DbTypeKind.dbArray, name: "name[]") + of 1005: return DbType(kind: DbTypeKind.dbArray, name: "int2[]") + of 1006: return DbType(kind: DbTypeKind.dbArray, name: "int2vector[]") + of 1007: return DbType(kind: DbTypeKind.dbArray, name: "int4[]") + of 1008: return DbType(kind: DbTypeKind.dbArray, name: "regproc[]") + of 1009: return DbType(kind: DbTypeKind.dbArray, name: "text[]") + of 1028: return DbType(kind: DbTypeKind.dbArray, name: "oid[]") + of 1010: return DbType(kind: DbTypeKind.dbArray, name: "tid[]") + of 1011: return DbType(kind: DbTypeKind.dbArray, name: "xid[]") + of 1012: return DbType(kind: DbTypeKind.dbArray, name: "cid[]") + of 1013: return DbType(kind: DbTypeKind.dbArray, name: "oidvector[]") + of 1014: return DbType(kind: DbTypeKind.dbArray, name: "bpchar[]") + of 1015: return DbType(kind: DbTypeKind.dbArray, name: "varchar[]") + of 1016: return DbType(kind: DbTypeKind.dbArray, name: "int8[]") + of 1017: return DbType(kind: DbTypeKind.dbArray, name: "point[]") + of 1018: return DbType(kind: DbTypeKind.dbArray, name: "lseg[]") + of 1019: return DbType(kind: DbTypeKind.dbArray, name: "path[]") + of 1020: return DbType(kind: DbTypeKind.dbArray, name: "box[]") + of 1021: return DbType(kind: DbTypeKind.dbArray, name: "float4[]") + of 1022: return DbType(kind: DbTypeKind.dbArray, name: "float8[]") + of 1023: return DbType(kind: DbTypeKind.dbArray, name: "abstime[]") + of 1024: return DbType(kind: DbTypeKind.dbArray, name: "reltime[]") + of 1025: return DbType(kind: DbTypeKind.dbArray, name: "tinterval[]") + of 1027: return DbType(kind: DbTypeKind.dbArray, name: "polygon[]") + of 1040: return DbType(kind: DbTypeKind.dbArray, name: "macaddr[]") + of 1041: return DbType(kind: DbTypeKind.dbArray, name: "inet[]") + of 1263: return DbType(kind: DbTypeKind.dbArray, name: "cstring[]") + of 1115: return DbType(kind: DbTypeKind.dbArray, name: "timestamp[]") + of 1182: return DbType(kind: DbTypeKind.dbArray, name: "date[]") + of 1183: return DbType(kind: DbTypeKind.dbArray, name: "time[]") + of 1185: return DbType(kind: DbTypeKind.dbArray, name: "timestamptz[]") + of 1187: return DbType(kind: DbTypeKind.dbArray, name: "interval[]") + of 1231: return DbType(kind: DbTypeKind.dbArray, name: "numeric[]") + of 1270: return DbType(kind: DbTypeKind.dbArray, name: "timetz[]") + of 1561: return DbType(kind: DbTypeKind.dbArray, name: "bit[]") + of 1563: return DbType(kind: DbTypeKind.dbArray, name: "varbit[]") + of 2201: return DbType(kind: DbTypeKind.dbArray, name: "refcursor[]") + of 2951: return DbType(kind: DbTypeKind.dbArray, name: "uuid[]") + of 3643: return DbType(kind: DbTypeKind.dbArray, name: "tsvector[]") + of 3645: return DbType(kind: DbTypeKind.dbArray, name: "tsquery[]") + of 3807: return DbType(kind: DbTypeKind.dbArray, name: "jsonb[]") + of 2949: return DbType(kind: DbTypeKind.dbArray, name: "txid_snapshot[]") + of 3905: return DbType(kind: DbTypeKind.dbArray, name: "int4range[]") + of 3907: return DbType(kind: DbTypeKind.dbArray, name: "numrange[]") + of 3909: return DbType(kind: DbTypeKind.dbArray, name: "tsrange[]") + of 3911: return DbType(kind: DbTypeKind.dbArray, name: "tstzrange[]") + of 3913: return DbType(kind: DbTypeKind.dbArray, name: "daterange[]") + of 3927: return DbType(kind: DbTypeKind.dbArray, name: "int8range[]") + of 2287: return DbType(kind: DbTypeKind.dbArray, name: "record[]") + + of 705: return DbType(kind: DbTypeKind.dbUnknown, name: "unknown") + else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced. + +proc buildBaseDbColumns*(res: PPGresult; cols: int32): DbColumns = + result = newSeqOfCap[DbColumn](cols.int) + setLen(result, cols) + for col in 0'i32 ..< cols: + result[col].name = $pqfname(res, col) + result[col].typ = getBaseColumnType(res, col) + result[col].tableName = $(pqftable(res, col)) + +proc appendDbRowWithBaseColumns*(res: PPGresult; dbRows: var DbRows; line, cols: int32; base: DbColumns) = + var columns = base + for col in 0'i32 ..< cols: + if pqgetisnull(res, line, col) == 1: + columns[col].typ = DbType(kind: dbNull, name: "null") + dbRows.add(columns) + +proc setColumnInfo*(res: PPGresult; dbRows: var DbRows; line, cols: int32) = + let base = buildBaseDbColumns(res, cols) + appendDbRowWithBaseColumns(res, dbRows, line, cols, base) + +proc newRow*(L: int): Row = + newSeq(result, L) + for i in 0..L-1: result[i] = "" + +proc setRow*(res: PPGresult, r: var Row, line, cols: int32) = + for col in 0'i32..cols-1: + setLen(r[col], 0) + let x = pqgetvalue(res, line, col) + if x.isNil: + r[col] = "" + else: + add(r[col], x) + +proc dbQuote(s: string): string = + # ## DB quotes the string. + # if s == "null": + # return "NULL" + # result = "'" + # for c in items(s): + # if c == '\'': add(result, "''") + # else: add(result, c) + # add(result, '\'') + + ## DB quotes the string. + if s == "null": + return "NULL" + result = newStringOfCap(s.len * 2 + 2) + result.add('\'') + for c in items(s): + case c + of '\'': add(result, "''") + of '\0': add(result, "\\0") + else: add(result, c) + add(result, '\'') + +proc dbFormat*(formatstr: string, args: varargs[string]): string = + var a = 0 + if args.len > 0 and not formatstr.contains("?"): + dbError("""parameter substitution expects "?" """) + if args.len == 0: + return formatstr + result = newStringOfCap(formatstr.len + args.len * 8) + var segStart = 0 + for j in 0 ..< formatstr.len: + if formatstr[j] == '?': + if j > segStart: + result.add(formatstr[segStart ..< j]) + result.add(dbQuote(args[a])) + inc(a) + segStart = j + 1 + if segStart < formatstr.len: + result.add(formatstr[segStart ..< formatstr.len]) + + +proc questionToDaller*(s:string):string = + ## from `UPDATE user SET name = ?, email = ? WHERE id = ?` + ## + ## to `UPDATE user SET name = $1, email = $2 WHERE id = $3` + var i = 1 + var segStart = 0 + result = newStringOfCap(s.len + 8) + for j in 0 ..< s.len: + if s[j] == '?': + if j > segStart: + result.add(s[segStart ..< j]) + result.add('$') + result.add($i) + inc(i) + segStart = j + 1 + if segStart < s.len: + result.add(s[segStart ..< s.len]) + + +type PGParams* = object + nParams*: int32 + values*: cstringArray + lengths*: seq[int32] + formats*: seq[int32] # 0:text,1:binary + + +proc objArrayParamSeqs(args: JsonNode; columns: seq[Row]): tuple[values: seq[string], lengths: seq[int32], formats: seq[int32]] = + result.values = newSeq[string](args.len) + result.lengths = newSeq[int32](args.len) + result.formats = newSeq[int32](args.len) + var i = 0 + for arg in args.items: + defer: i.inc() + case arg["value"].kind + of JBool: + result.values[i] = if arg["value"].getBool: "t" else: "f" + result.lengths[i] = 0 + result.formats[i] = 0 + of JInt: + result.values[i] = $arg["value"].getInt + result.lengths[i] = 0 + result.formats[i] = 0 + of JFloat: + result.values[i] = $arg["value"].getFloat + result.lengths[i] = 0 + result.formats[i] = 0 + of JNull: + result.values[i] = "NULL" + result.lengths[i] = 0 + result.formats[i] = 0 + of JObject, JArray: + result.values[i] = arg["value"].pretty + result.lengths[i] = 0 + result.formats[i] = 0 + of JString: + if columns.len > 0: + for column in columns: + if column[0] == arg["key"].getStr: + defer: break + let value = arg["value"].getStr + result.values[i] = value + result.lengths[i] = value.len.int32 + if column[1] == "bytea": + result.formats[i] = 1 + else: + result.formats[i] = 0 + else: + let value = arg["value"].getStr + result.values[i] = value + result.lengths[i] = value.len.int32 + result.formats[i] = 0 + +proc allocPgParamsFromSeqs(values: seq[string]; lengths, formats: seq[int32]; n: int): PGParams = + result.nParams = n.int32 + result.lengths = lengths + result.formats = formats + result.values = allocCStringArray(values) + for j, row in values: + if row == "NULL": + result.values[j] = nil + +proc fromObjArray*(_: type PGParams, args: JsonNode, columns: seq[Row]): PGParams = + if args.len == 0: + return + let t = objArrayParamSeqs(args, columns) + result = allocPgParamsFromSeqs(t.values, t.lengths, t.formats, args.len) + +proc fromObjArray*(_: type PGParams, args: JsonNode): PGParams = + ## `args` is JArray `[{"key": "bool", "value": true},{"key": "int", "value": 1}]` + if args.len == 0: + return + let t = objArrayParamSeqs(args, @[]) + result = allocPgParamsFromSeqs(t.values, t.lengths, t.formats, args.len) + + +proc fromArray*(_:type PGParams, args: JsonNode):PGParams = + ## `args` is JArray `{true, 1, 1.1, "alice"}` + if args.len == 0: + return + result.nParams = args.len.int32 + + var values = newSeq[string](args.len) + result.formats = newSeq[int32](args.len) + result.lengths = newSeq[int32](args.len) + + var i = 0 + for arg in args.items: + defer: i.inc() + case arg.kind + of JBool: + values[i] = if arg.getBool: "t" else: "f" + result.lengths[i] = 0 + result.formats[i] = 0 + of JInt: + values[i] = $arg.getInt + result.lengths[i] = 0 + result.formats[i] = 0 + of JFloat: + values[i] = $arg.getFloat + result.lengths[i] = 0 + result.formats[i] = 0 + of JNull: + values[i] = "NULL" + result.lengths[i] = 0 + result.formats[i] = 0 + of JObject, JArray: + values[i] = arg.pretty + result.lengths[i] = 0 + result.formats[i] = 0 + of JString: + let value = arg.getStr + values[i] = value + result.lengths[i] = value.len.int32 + result.formats[i] = 0 + + result.values = allocCStringArray(values) + for i, row in values: + if row == "NULL": + result.values[i] = nil diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_rdb.nim b/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_rdb.nim new file mode 100644 index 0000000..31ca49c --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/postgres/postgres_rdb.nim @@ -0,0 +1,374 @@ +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(windows): + const + dllName = "libpq.dll" +elif defined(macosx): + const + dllName = "libpq.dylib" +else: + const + dllName = "libpq.so(.5|)" +type + POid* = ptr Oid + Oid* = int32 + +const + ERROR_MSG_LENGTH* = 4096 + CMDSTATUS_LEN* = 40 + +type + SockAddr* = array[1..112, int8] + PGresAttDesc*{.pure, final.} = object + name*: cstring + adtid*: Oid + adtsize*: int + + PPGresAttDesc* = ptr PGresAttDesc + PPPGresAttDesc* = ptr PPGresAttDesc + PGresAttValue*{.pure, final.} = object + length*: int32 + value*: cstring + + PPGresAttValue* = ptr PGresAttValue + PPPGresAttValue* = ptr PPGresAttValue + PExecStatusType* = ptr ExecStatusType + ExecStatusType* = enum + PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PGRES_COPY_OUT, + PGRES_COPY_IN, PGRES_BAD_RESPONSE, PGRES_NONFATAL_ERROR, PGRES_FATAL_ERROR, + PGRES_COPY_BOTH, PGRES_SINGLE_TUPLE + PGlobjfuncs*{.pure, final.} = object + fn_lo_open*: Oid + fn_lo_close*: Oid + fn_lo_creat*: Oid + fn_lo_unlink*: Oid + fn_lo_lseek*: Oid + fn_lo_tell*: Oid + fn_lo_read*: Oid + fn_lo_write*: Oid + + PPGlobjfuncs* = ptr PGlobjfuncs + PConnStatusType* = ptr ConnStatusType + ConnStatusType* = enum + CONNECTION_OK, CONNECTION_BAD, CONNECTION_STARTED, CONNECTION_MADE, + CONNECTION_AWAITING_RESPONSE, CONNECTION_AUTH_OK, CONNECTION_SETENV, + CONNECTION_SSL_STARTUP, CONNECTION_NEEDED, CONNECTION_CHECK_WRITABLE, + CONNECTION_CONSUME, CONNECTION_GSS_STARTUP, CONNECTION_CHECK_TARGET + PGconn*{.pure, final.} = object + pghost*: cstring + pgtty*: cstring + pgport*: cstring + pgoptions*: cstring + dbName*: cstring + status*: ConnStatusType + errorMessage*: array[0..(ERROR_MSG_LENGTH) - 1, char] + Pfin*: File + Pfout*: File + Pfdebug*: File + sock*: int32 + laddr*: SockAddr + raddr*: SockAddr + salt*: array[0..(2) - 1, char] + asyncNotifyWaiting*: int32 + notifyList*: pointer + pguser*: cstring + pgpass*: cstring + lobjfuncs*: PPGlobjfuncs + + PPGconn* = ptr PGconn + PGresult*{.pure, final.} = object + ntups*: int32 + numAttributes*: int32 + attDescs*: PPGresAttDesc + tuples*: PPPGresAttValue + tupArrSize*: int32 + resultStatus*: ExecStatusType + cmdStatus*: array[0..(CMDSTATUS_LEN) - 1, char] + binary*: int32 + conn*: PPGconn + + PGCancel*{.pure, final.} = object + PPGcancel* = ptr PGCancel + + PPGresult* = ptr PGresult + PPostgresPollingStatusType* = ptr PostgresPollingStatusType + PostgresPollingStatusType* = enum + PGRES_POLLING_FAILED = 0, PGRES_POLLING_READING, PGRES_POLLING_WRITING, + PGRES_POLLING_OK, PGRES_POLLING_ACTIVE + PPGTransactionStatusType* = ptr PGTransactionStatusType + PGTransactionStatusType* = enum + PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR, + PQTRANS_UNKNOWN + PPGVerbosity* = ptr PGVerbosity + PGVerbosity* = enum + PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE, PQERRORS_SQLSTATE + PPGNotify* = ptr pgNotify + pgNotify*{.pure, final.} = object + relname*: cstring + be_pid*: int32 + extra*: cstring + + PQnoticeReceiver* = proc (arg: pointer, res: PPGresult){.cdecl.} + PQnoticeProcessor* = proc (arg: pointer, message: cstring){.cdecl.} + Ppqbool* = ptr pqbool + pqbool* = char + PPQprintOpt* = ptr PQprintOpt + PQprintOpt*{.pure, final.} = object + header*: pqbool + align*: pqbool + standard*: pqbool + html3*: pqbool + expanded*: pqbool + pager*: pqbool + fieldSep*: cstring + tableOpt*: cstring + caption*: cstring + fieldName*: ptr cstring + + PPQconninfoOption* = ptr PQconninfoOption + PQconninfoOption*{.pure, final.} = object + keyword*: cstring + envvar*: cstring + compiled*: cstring + val*: cstring + label*: cstring + dispchar*: cstring + dispsize*: int32 + + PPQArgBlock* = ptr PQArgBlock + PQArgBlock*{.pure, final.} = object + length*: int32 + isint*: int32 + p*: pointer + +proc pqinitOpenSSL*(do_ssl: int32, do_crypto: int32) {.cdecl, dynlib: dllName, + importc: "PQinitOpenSSL".} +proc pqconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, + importc: "PQconnectStart".} +proc pqconnectPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, + dynlib: dllName, importc: "PQconnectPoll".} +proc pqconnectdb*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName, + importc: "PQconnectdb".} +proc pqsetdbLogin*(pghost: cstring, pgport: cstring, pgoptions: cstring, + pgtty: cstring, dbName: cstring, login: cstring, pwd: cstring): PPGconn{. + cdecl, dynlib: dllName, importc: "PQsetdbLogin".} +proc pqsetdb*(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn +proc pqfinish*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQfinish".} +proc pqconndefaults*(): PPQconninfoOption{.cdecl, dynlib: dllName, + importc: "PQconndefaults".} +proc pqconninfoFree*(connOptions: PPQconninfoOption){.cdecl, dynlib: dllName, + importc: "PQconninfoFree".} +proc pqresetStart*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQresetStart".} +proc pqresetPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl, + dynlib: dllName, importc: "PQresetPoll".} +proc pqreset*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQreset".} + +# add these procs pqGetCancel, pqFreeCancel and pqCancel. +# https://www.postgresql.org/docs/12/libpq-cancel.html +proc pqGetCancel*(conn: PPGconn): PPGcancel{.cdecl, dynlib: dllName, importc: "PQgetCancel".} +proc pqFreeCancel*(cancel: PPGcancel){.cdecl, dynlib: dllName, importc: "PQfreeCancel".} +proc pqCancel*(cancel: PPGcancel, errbuf: cstring, errbufsize: int32):int32{.cdecl, dynlib: dllName, importc: "PQcancel".} + +proc pqrequestCancel*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQrequestCancel".} +proc pqdb*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQdb".} +proc pquser*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQuser".} +proc pqpass*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQpass".} +proc pqhost*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQhost".} +proc pqport*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQport".} +proc pqtty*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQtty".} +proc pqoptions*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, + importc: "PQoptions".} +proc pqstatus*(conn: PPGconn): ConnStatusType{.cdecl, dynlib: dllName, + importc: "PQstatus".} +proc pqtransactionStatus*(conn: PPGconn): PGTransactionStatusType{.cdecl, + dynlib: dllName, importc: "PQtransactionStatus".} +proc pqparameterStatus*(conn: PPGconn, paramName: cstring): cstring{.cdecl, + dynlib: dllName, importc: "PQparameterStatus".} +proc pqserverVersion*(conn: PPGconn): int32{.cdecl, + dynlib: dllName, importc: "PQserverVersion".} +proc pqprotocolVersion*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQprotocolVersion".} +proc pqerrorMessage*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, + importc: "PQerrorMessage".} +proc pqsocket*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQsocket".} +proc pqbackendPID*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQbackendPID".} +proc pqconnectionNeedsPassword*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQconnectionNeedsPassword".} +proc pqconnectionUsedPassword*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQconnectionUsedPassword".} +proc pqclientEncoding*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQclientEncoding".} +proc pqsetClientEncoding*(conn: PPGconn, encoding: cstring): int32{.cdecl, + dynlib: dllName, importc: "PQsetClientEncoding".} +when defined(USE_SSL): + # Get the SSL structure associated with a connection + proc pqgetssl*(conn: PPGconn): PSSL{.cdecl, dynlib: dllName, + importc: "PQgetssl".} +proc pqsetErrorVerbosity*(conn: PPGconn, verbosity: PGVerbosity): PGVerbosity{. + cdecl, dynlib: dllName, importc: "PQsetErrorVerbosity".} +proc pqtrace*(conn: PPGconn, debug_port: File){.cdecl, dynlib: dllName, + importc: "PQtrace".} +proc pquntrace*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQuntrace".} +proc pqsetNoticeReceiver*(conn: PPGconn, theProc: PQnoticeReceiver, arg: pointer): PQnoticeReceiver{. + cdecl, dynlib: dllName, importc: "PQsetNoticeReceiver".} +proc pqsetNoticeProcessor*(conn: PPGconn, theProc: PQnoticeProcessor, + arg: pointer): PQnoticeProcessor{.cdecl, + dynlib: dllName, importc: "PQsetNoticeProcessor".} +proc pqexec*(conn: PPGconn, query: cstring): PPGresult{.cdecl, dynlib: dllName, + importc: "PQexec".} +proc pqexecParams*(conn: PPGconn, command: cstring, nParams: int32, + paramTypes: POid, paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{. + cdecl, dynlib: dllName, importc: "PQexecParams".} +proc pqprepare*(conn: PPGconn, stmtName, query: cstring, nParams: int32, + paramTypes: POid): PPGresult{.cdecl, dynlib: dllName, importc: "PQprepare".} +proc pqexecPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, + paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{. + cdecl, dynlib: dllName, importc: "PQexecPrepared".} +proc pqsendQuery*(conn: PPGconn, query: cstring): int32{.cdecl, dynlib: dllName, + importc: "PQsendQuery".} + ## See also https://www.postgresql.org/docs/current/libpq-async.html +proc pqsendQueryParams*(conn: PPGconn, command: cstring, nParams: int32, + paramTypes: POid, paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, + resultFormat: int32): int32{.cdecl, dynlib: dllName, + importc: "PQsendQueryParams".} +proc pqsendPrepare*(conn: PPGconn, stmtName: cstring, query: cstring, + nParams: int32, paramTypes: POid): int32{.cdecl, dynlib: dllName, + importc: "PQsendPrepare".} +proc pqsendQueryPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32, + paramValues: cstringArray, + paramLengths, paramFormats: ptr int32, + resultFormat: int32): int32{.cdecl, dynlib: dllName, + importc: "PQsendQueryPrepared".} +proc pqSetSingleRowMode*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQsetSingleRowMode".} + ## See also https://www.postgresql.org/docs/current/libpq-single-row-mode.html +proc pqgetResult*(conn: PPGconn): PPGresult{.cdecl, dynlib: dllName, + importc: "PQgetResult".} +proc pqisBusy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQisBusy".} +proc pqconsumeInput*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQconsumeInput".} +proc pqnotifies*(conn: PPGconn): PPGNotify{.cdecl, dynlib: dllName, + importc: "PQnotifies".} +proc pqputCopyData*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{. + cdecl, dynlib: dllName, importc: "PQputCopyData".} +proc pqputCopyEnd*(conn: PPGconn, errormsg: cstring): int32{.cdecl, + dynlib: dllName, importc: "PQputCopyEnd".} +proc pqgetCopyData*(conn: PPGconn, buffer: cstringArray, async: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetCopyData".} +proc pqgetline*(conn: PPGconn, str: cstring, len: int32): int32{.cdecl, + dynlib: dllName, importc: "PQgetline".} +proc pqputline*(conn: PPGconn, str: cstring): int32{.cdecl, dynlib: dllName, + importc: "PQputline".} +proc pqgetlineAsync*(conn: PPGconn, buffer: cstring, bufsize: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetlineAsync".} +proc pqputnbytes*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.cdecl, + dynlib: dllName, importc: "PQputnbytes".} +proc pqendcopy*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQendcopy".} +proc pqsetnonblocking*(conn: PPGconn, arg: int32): int32{.cdecl, + dynlib: dllName, importc: "PQsetnonblocking".} +proc pqisnonblocking*(conn: PPGconn): int32{.cdecl, dynlib: dllName, + importc: "PQisnonblocking".} +proc pqflush*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQflush".} +proc pqfn*(conn: PPGconn, fnid: int32, result_buf, result_len: ptr int32, + result_is_int: int32, args: PPQArgBlock, nargs: int32): PPGresult{. + cdecl, dynlib: dllName, importc: "PQfn".} +proc pqresultStatus*(res: PPGresult): ExecStatusType{.cdecl, dynlib: dllName, + importc: "PQresultStatus".} +proc pqresStatus*(status: ExecStatusType): cstring{.cdecl, dynlib: dllName, + importc: "PQresStatus".} +proc pqresultErrorMessage*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQresultErrorMessage".} +proc pqresultErrorField*(res: PPGresult, fieldcode: int32): cstring{.cdecl, + dynlib: dllName, importc: "PQresultErrorField".} +proc pqntuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, + importc: "PQntuples".} +proc pqnfields*(res: PPGresult): int32{.cdecl, dynlib: dllName, + importc: "PQnfields".} +proc pqbinaryTuples*(res: PPGresult): int32{.cdecl, dynlib: dllName, + importc: "PQbinaryTuples".} +proc pqfname*(res: PPGresult, field_num: int32): cstring{.cdecl, + dynlib: dllName, importc: "PQfname".} +proc pqfnumber*(res: PPGresult, field_name: cstring): int32{.cdecl, + dynlib: dllName, importc: "PQfnumber".} +proc pqftable*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, + importc: "PQftable".} +proc pqftablecol*(res: PPGresult, field_num: int32): int32{.cdecl, + dynlib: dllName, importc: "PQftablecol".} +proc pqfformat*(res: PPGresult, field_num: int32): int32{.cdecl, + dynlib: dllName, importc: "PQfformat".} +proc pqftype*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName, + importc: "PQftype".} +proc pqfsize*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, + importc: "PQfsize".} +proc pqfmod*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName, + importc: "PQfmod".} +proc pqcmdStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQcmdStatus".} +proc pqoidStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQoidStatus".} +proc pqoidValue*(res: PPGresult): Oid{.cdecl, dynlib: dllName, + importc: "PQoidValue".} +proc pqcmdTuples*(res: PPGresult): cstring{.cdecl, dynlib: dllName, + importc: "PQcmdTuples".} +proc pqgetvalue*(res: PPGresult, tup_num: int32, field_num: int32): cstring{. + cdecl, dynlib: dllName, importc: "PQgetvalue".} +proc pqgetlength*(res: PPGresult, tup_num: int32, field_num: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetlength".} +proc pqgetisnull*(res: PPGresult, tup_num: int32, field_num: int32): int32{. + cdecl, dynlib: dllName, importc: "PQgetisnull".} +proc pqclear*(res: PPGresult){.cdecl, dynlib: dllName, importc: "PQclear".} +proc pqfreemem*(p: pointer){.cdecl, dynlib: dllName, importc: "PQfreemem".} +proc pqmakeEmptyPGresult*(conn: PPGconn, status: ExecStatusType): PPGresult{. + cdecl, dynlib: dllName, importc: "PQmakeEmptyPGresult".} +proc pqescapeString*(till, `from`: cstring, len: int): int{.cdecl, + dynlib: dllName, importc: "PQescapeString".} +proc pqescapeBytea*(bintext: cstring, binlen: int, bytealen: var int): cstring{. + cdecl, dynlib: dllName, importc: "PQescapeBytea".} +proc pqunescapeBytea*(strtext: cstring, retbuflen: var int): cstring{.cdecl, + dynlib: dllName, importc: "PQunescapeBytea".} +proc pqprint*(fout: File, res: PPGresult, ps: PPQprintOpt){.cdecl, + dynlib: dllName, importc: "PQprint".} +proc pqdisplayTuples*(res: PPGresult, fp: File, fillAlign: int32, + fieldSep: cstring, printHeader: int32, quiet: int32){. + cdecl, dynlib: dllName, importc: "PQdisplayTuples".} +proc pqprintTuples*(res: PPGresult, fout: File, printAttName: int32, + terseOutput: int32, width: int32){.cdecl, dynlib: dllName, + importc: "PQprintTuples".} +proc lo_open*(conn: PPGconn, lobjId: Oid, mode: int32): int32{.cdecl, + dynlib: dllName, importc: "lo_open".} +proc lo_close*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName, + importc: "lo_close".} +proc lo_read*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{. + cdecl, dynlib: dllName, importc: "lo_read".} +proc lo_write*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{. + cdecl, dynlib: dllName, importc: "lo_write".} +proc lo_lseek*(conn: PPGconn, fd: int32, offset: int32, whence: int32): int32{. + cdecl, dynlib: dllName, importc: "lo_lseek".} +proc lo_creat*(conn: PPGconn, mode: int32): Oid{.cdecl, dynlib: dllName, + importc: "lo_creat".} +proc lo_tell*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName, + importc: "lo_tell".} +proc lo_unlink*(conn: PPGconn, lobjId: Oid): int32{.cdecl, dynlib: dllName, + importc: "lo_unlink".} +proc lo_import*(conn: PPGconn, filename: cstring): Oid{.cdecl, dynlib: dllName, + importc: "lo_import".} +proc lo_export*(conn: PPGconn, lobjId: Oid, filename: cstring): int32{.cdecl, + dynlib: dllName, importc: "lo_export".} +proc pqmblen*(s: cstring, encoding: int32): int32{.cdecl, dynlib: dllName, + importc: "PQmblen".} +proc pqenv2encoding*(): int32{.cdecl, dynlib: dllName, importc: "PQenv2encoding".} +proc pqsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn = + result = pqsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, "", "") + +when defined(nimHasStyleChecks): + {.pop.} diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_impl.nim b/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_impl.nim new file mode 100644 index 0000000..14f154e --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_impl.nim @@ -0,0 +1,263 @@ +import std/asyncdispatch +import std/strutils +import std/json +import ../../models/database_types +import ../../prepared_param +import ./sqlite_rdb +import ./sqlite_lib + + +proc query*(db:PSqlite3, query:string, args:seq[string], timeout:int):Future[(seq[Row], DbRows)] {.async.} = + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + var dbRows: DbRows + var rows = newSeq[seq[string]]() + for row in db.instantRows(dbRows, query, args): + var columns = newSeq[string](row.len) + for i in 0..row.len()-1: + columns[i] = row[i] + rows.add(columns) + return (rows, dbRows) + + +proc queryPlain*(db:PSqlite3, query:string, args:seq[string], timeout:int):Future[seq[Row]] {.async.} = + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + var rows = newSeq[seq[string]]() + for row in db.instantRowsPlain(query, args): + var columns = newSeq[string](row.len) + for i in 0..row.len()-1: + columns[i] = row[i] + rows.add(columns) + return rows + + +proc getColumnTypes*(db:PSqlite3, query: string):Future[seq[(string, string)]] {.async.} = + sleepAsync(0).await + var dbRows: DbRows + var columns = newSeq[(string, string)]() + for row in db.instantRows(dbRows, query, newSeq[string]()): + columns.add((row[1], row[2])) + return columns + + +proc exec*(db:PSqlite3, query: string, args: JsonNode, columns:seq[(string, string)], timeout:int) {.async.} = + ## args is `JArray` + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + # var q = dbFormat(query, strArges) + var stmt: PStmt + var res:bool + if prepare_v2(db, query.cstring, query.len.cint, stmt, nil) == SQLITE_OK: + var argCount = 1 + for arg in args.items: + defer: argCount.inc() + case arg["value"].kind + of JBool: + let value = arg["value"].getBool + let insertVal = if value:1 else: 0 + discard bind_int64(stmt, argCount.int32, insertVal) + of JInt: + let value = arg["value"].getInt + discard bind_int64(stmt, argCount.int32, value) + of JFloat: + let value = arg["value"].getFloat + discard bind_double(stmt, argCount.int32, value.float64) + of JNull: + discard bind_null(stmt, argCount.int32) + of JObject, JArray: + let value = arg["value"].pretty + discard bind_text(stmt, argCount.int32, value.cstring, value.len.int32, SQLITE_TRANSIENT) + of JString: + for column in columns: + let columnName = column[0] + let columnTyp = column[1] + if columnName == arg["key"].getStr: + defer: break + if columnTyp == "BLOB": + let value = arg["value"].getStr + var blobVal:seq[byte] + for c in value: + blobVal.add(c.byte) + discard bind_blob(stmt, argCount.int32, blobVal[0].unsafeAddr, blobVal.len.int32, SQLITE_TRANSIENT) + else: + let value = arg["value"].getStr + discard bind_text(stmt, argCount.int32, value.cstring, value.len.int32, SQLITE_TRANSIENT) + + let x = step(stmt) + if x in {SQLITE_DONE, SQLITE_ROW}: + res = finalize(stmt) == SQLITE_OK + else: + discard finalize(stmt) + res = false + if not res: + dbError(db) + + +proc exec*(db:PSqlite3, query: string, args: JsonNode, timeout:int) {.async.} = + ## used for rdb.raw().exec() + ## args are `JArray` + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + var stmt: PStmt + var res:bool + if prepare_v2(db, query.cstring, query.len.cint, stmt, nil) == SQLITE_OK: + var argCount = 1 + for arg in args.items: + defer: argCount.inc() + case arg.kind + of JBool: + let value = arg.getBool + let insertVal = if value:1 else: 0 + discard bind_int64(stmt, argCount.int32, insertVal) + of JInt: + let value = arg.getInt + discard bind_int64(stmt, argCount.int32, value) + of JFloat: + let value = arg.getFloat + discard bind_double(stmt, argCount.int32, value.float64) + of JNull: + discard bind_null(stmt, argCount.int32) + of JObject, JArray: + let value = arg.pretty + discard bind_text(stmt, argCount.int32, value.cstring, value.len.int32, SQLITE_TRANSIENT) + of JString: + let value = arg.getStr + discard bind_text(stmt, argCount.int32, value.cstring, value.len.int32, SQLITE_TRANSIENT) + + let x = step(stmt) + if x in {SQLITE_DONE, SQLITE_ROW}: + res = finalize(stmt) == SQLITE_OK + else: + discard finalize(stmt) + res = false + if not res: + dbError(db) + + +proc exec*(db:PSqlite3, query: string, args: seq[string], timeout:int) {.async.} = + ## Not used anymore + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + var q = dbFormat(query, args) + var stmt: PStmt + var res:bool + if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK: + let x = step(stmt) + if x in {SQLITE_DONE, SQLITE_ROW}: + res = finalize(stmt) == SQLITE_OK + else: + discard finalize(stmt) + res = false + if not res: + dbError(db) + + +proc getColumns*(db:PSqlite3, query:string, args:seq[string], timeout:int):Future[seq[string]] {.async.} = + assert(not db.isNil, "Database not connected.") + var dbRows: DbRows + return db.getColumns(dbRows, query, args) + + +proc prepare*(db:PSqlite3, query:string, timeout:int):Future[PStmt] {.async.} = + if prepare_v2(db, query, query.len.cint, result, nil) != SQLITE_OK: + discard finalize(result) + dbError(db) + + +proc bindPreparedParams(db: PSqlite3, stmt: PStmt, args: seq[PreparedParam]) = + if reset(stmt) != SQLITE_OK: + dbError(db) + if clear_bindings(stmt) != SQLITE_OK: + dbError(db) + + for i, arg in args: + let paramIdx = i.int32 + 1 + if arg.isNull: + if bind_null(stmt, paramIdx) != SQLITE_OK: + dbError(db) + else: + if bind_text(stmt, paramIdx, arg.value.cstring, arg.value.len.int32, SQLITE_TRANSIENT) != SQLITE_OK: + dbError(db) + + +proc preparedQueryReuse*(db: PSqlite3, stmt: PStmt, args: seq[PreparedParam], timeout: int, + cachedColumns: DbColumns): Future[(seq[Row], DbRows)] {.async.} = + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + bindPreparedParams(db, stmt, args) + defer: + discard clear_bindings(stmt) + + var dbRows: DbRows + var rows = newSeq[seq[string]]() + + while true: + let stepRes = step(stmt) + if stepRes == SQLITE_ROW: + var columns = cachedColumns + setColumnsRuntimeTypes(columns, stmt) + dbRows.add(columns) + var row = newSeq[string](int(column_count(stmt))) + for i in 0 ..< row.len: + let text = column_text(stmt, i.int32) + if text.isNil: + row[i] = "" + else: + row[i] = $text + rows.add(row) + continue + if stepRes == SQLITE_DONE: + break + dbError(db) + + return (rows, dbRows) + + +proc preparedExecReuse*(db: PSqlite3, stmt: PStmt, args: seq[PreparedParam], timeout: int) {.async.} = + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + bindPreparedParams(db, stmt, args) + defer: + discard clear_bindings(stmt) + + var stepRes = step(stmt) + while stepRes == SQLITE_ROW: + stepRes = step(stmt) + if stepRes != SQLITE_DONE: + dbError(db) + + +proc preparedQuery*(db:PSqlite3, args:seq[string] = @[], sqliteStmt:PStmt):Future[(seq[Row], DbRows)] {.async.} = + # bind params + for i, row in args: + sqliteStmt.bindParam(i+1, row) + # run query + assert(not db.isNil, "Database not connected.") + sleepAsync(0).await + var dbRows: DbRows + var rows = newSeq[seq[string]]() + for row in db.instantRows(dbRows, sqliteStmt): + await sleepAsync(0) + var columns = newSeq[string](row.len) + for i in 0..row.len()-1: + columns[i] = row[i] + rows.add(columns) + return (rows, dbRows) + + +proc preparedExec*(db:PSqlite3, args:seq[string] = @[], sqliteStmt:PStmt) {.async.} = + # bind params + for i, row in args: + sqliteStmt.bindParam(i+1, row) + # run query + sleepAsync(0).await + let x = step(sqliteStmt) + var res:bool + if x in {SQLITE_DONE, SQLITE_ROW}: + res = true + else: + res = false + discard finalize(sqliteStmt) + if not res: + dbError(db) diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_lib.nim b/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_lib.nim new file mode 100644 index 0000000..07238bf --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_lib.nim @@ -0,0 +1,200 @@ +import std/macros +import ../../error +import ../../models/database_types +import ./sqlite_rdb + + +proc dbQuote(s: string): string = + ## Escapes the `'` (single quote) char to `''`. + ## Because single quote is used for defining `VARCHAR` in SQL. + runnableExamples: + doAssert dbQuote("'") == "''''" + doAssert dbQuote("A Foobar's pen.") == "'A Foobar''s pen.'" + + if s == "null": + return "NULL" + result = newStringOfCap(s.len * 2 + 2) + result.add('\'') + for c in items(s): + if c == '\'': add(result, "''") + else: add(result, c) + add(result, '\'') + +proc sqliteQuoteIdent*(name: string): string = + ## `PRAGMA` 等で使う SQLite の二重引用符識別子(内部の `"` は `""` にエスケープ)。 + result = newStringOfCap(name.len + 2) + result.add('"') + for c in name: + if c == '"': + result.add("\"\"") + else: + result.add(c) + result.add('"') + +proc dbError*(db: PSqlite3) {.noreturn.} = + ## Raises a `DbError` exception. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## if not db.tryExec(sql"SELECT * FROM not_exist_table"): + ## dbError(db) + ## db.close() + var e: ref DbError + new(e) + e.msg = $errmsg(db) + raise e + +proc dbFormat*(formatstr: string, args: varargs[string]): string = + var a = 0 + result = newStringOfCap(formatstr.len + args.len * 8) + var segStart = 0 + for j in 0 ..< formatstr.len: + if formatstr[j] == '?': + if j > segStart: + result.add(formatstr[segStart ..< j]) + result.add(dbQuote(args[a])) + inc(a) + segStart = j + 1 + if segStart < formatstr.len: + result.add(formatstr[segStart ..< formatstr.len]) + +proc setupQuery(db: PSqlite3, query: string, args: varargs[string]): PStmt = + assert(not db.isNil, "Database not connected.") + var q = dbFormat(query, args) + if prepare_v2(db, q.cstring, q.len.cint, result, nil) != SQLITE_OK: dbError(db) + +proc toTypeKind(t: var DbType; x: int32) = + case x + of SQLITE_INTEGER: + t.kind = dbInt + t.size = 8 + of SQLITE_FLOAT: + t.kind = dbFloat + t.size = 8 + of SQLITE_BLOB: t.kind = dbBlob + of SQLITE_NULL: t.kind = dbNull + of SQLITE_TEXT: t.kind = dbVarchar + else: t.kind = dbUnknown + +proc setColumnsStaticMeta*(columns: var DbColumns; x: PStmt) = + ## ステップ前でも列名・宣言型・テーブル名は取得できる(行に依存しない)。 + let L = column_count(x) + setLen(columns, L.int) + for i in 0'i32 ..< L: + columns[i].name = $column_name(x, i) + columns[i].typ.name = $column_decltype(x, i) + columns[i].tableName = $column_table_name(x, i) + +proc setColumnsRuntimeTypes*(columns: var DbColumns; x: PStmt) = + ## 行ごとに変わりうるのは `column_type` のみ。 + let L = column_count(x) + for i in 0'i32 ..< L: + toTypeKind(columns[i].typ, column_type(x, i)) + +iterator instantRows*(db: PSqlite3; dbRows: var DbRows; query: string, args: seq[string]): InstantRow + {.tags: [ReadDbEffect].} = + ## Similar to `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_, + ## but sets information about columns to `columns`. + ## + ## **Examples:** + ## + ## .. code-block:: Nim + ## + ## let db = open("mytest.db", "", "", "") + ## + ## # Records of my_table: + ## # | id | name | + ## # |----|----------| + ## # | 1 | item#1 | + ## # | 2 | item#2 | + ## + ## var columns: DbColumns + ## for row in db.instantRows(columns, sql"SELECT * FROM my_table"): + ## discard + ## echo columns[0] + ## + ## # Output: + ## # (name: "id", tableName: "my_table", typ: (kind: dbNull, + ## # notNull: false, name: "INTEGER", size: 0, maxReprLen: 0, precision: 0, + ## # scale: 0, min: 0, max: 0, validValues: @[]), primaryKey: false, + ## # foreignKey: false) + ## + ## db.close() + var stmt = setupQuery(db, query, args) + try: + var columns: DbColumns + setColumnsStaticMeta(columns, stmt) + while step(stmt) == SQLITE_ROW: + setColumnsRuntimeTypes(columns, stmt) + dbRows.add(columns) + yield stmt + finally: + if finalize(stmt) != SQLITE_OK: dbError(db) + +iterator instantRowsPlain*(db: PSqlite3; query: string, args: seq[string]): InstantRow + {.tags: [ReadDbEffect].} = + var stmt = setupQuery(db, query, args) + try: + while step(stmt) == SQLITE_ROW: + yield stmt + finally: + if finalize(stmt) != SQLITE_OK: dbError(db) + +iterator instantRows*(db: PSqlite3, dbRows: var DbRows, sqliteStmt: PStmt): InstantRow + {.tags: [ReadDbEffect,WriteDbEffect].} = + var sqliteStmt = sqliteStmt + try: + var columns: DbColumns + setColumnsStaticMeta(columns, sqliteStmt) + while step(sqliteStmt) == SQLITE_ROW: + setColumnsRuntimeTypes(columns, sqliteStmt) + dbRows.add(columns) + yield sqliteStmt + finally: + if finalize(sqliteStmt) != SQLITE_OK: dbError(db) + +proc getColumns*(db: PSqlite3; dbRows: var DbRows; query: string, args: seq[string]):seq[string] = + var stmt = setupQuery(db, query, args) + try: + var i:int32 = 0 + while true: + let name = column_name(stmt, i) + if name.len == 0: break + result.add($name) + i.inc() + finally: + if finalize(stmt) != SQLITE_OK: dbError(db) + + return result + +proc len*(row: InstantRow): int32 {.inline.} = + ## Returns number of columns in a row. + ## + ## See also: + ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_ + ## example code + column_count(row) + +proc `[]`*(row: InstantRow, col: int32): string {.inline.} = + ## Returns text for given column of the row. + ## + ## See also: + ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_ + ## example code + $column_text(row, col) + +template dbBindParamError*(paramIdx: int, val: varargs[untyped]) = + ## Raises a `DbError` exception. + var e: ref DbError + new(e) + e.msg = "error binding param in position " & $paramIdx + raise e + +proc bindParam*(ps: PStmt, paramIdx: int, val: string, copy = true) = + ## Binds a string to the specified paramIndex. + ## if copy is true then SQLite makes its own private copy of the data immediately + if bind_text(ps, paramIdx.int32, val.cstring, val.len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK: + dbBindParamError(paramIdx, val) diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_rdb.nim b/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_rdb.nim new file mode 100644 index 0000000..6b9ef9e --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/sqlite/sqlite_rdb.nim @@ -0,0 +1,384 @@ +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +when defined(windows): + when defined(nimOldDlls): + const Lib = "sqlite3.dll" + elif defined(cpu64): + const Lib = "sqlite3_64.dll" + else: + const Lib = "sqlite3_32.dll" +elif defined(macosx): + const + Lib = "libsqlite3(|.0).dylib" +else: + const + Lib = "libsqlite3.so(|.0)" + +when defined(staticSqlite): + {.pragma: mylib.} + {.compile("sqlite3.c", "-O3").} +else: + {.pragma: mylib, dynlib: Lib.} + +const + SQLITE_INTEGER* = 1 + SQLITE_FLOAT* = 2 + SQLITE_BLOB* = 4 + SQLITE_NULL* = 5 + SQLITE_TEXT* = 3 + SQLITE_UTF8* = 1 + SQLITE_UTF16LE* = 2 + SQLITE_UTF16BE* = 3 # Use native byte order + SQLITE_UTF16* = 4 # sqlite3_create_function only + SQLITE_ANY* = 5 #sqlite_exec return values + SQLITE_OK* = 0 + SQLITE_ERROR* = 1 # SQL error or missing database + SQLITE_INTERNAL* = 2 # An internal logic error in SQLite + SQLITE_PERM* = 3 # Access permission denied + SQLITE_ABORT* = 4 # Callback routine requested an abort + SQLITE_BUSY* = 5 # The database file is locked + SQLITE_LOCKED* = 6 # A table in the database is locked + SQLITE_NOMEM* = 7 # A malloc() failed + SQLITE_READONLY* = 8 # Attempt to write a readonly database + SQLITE_INTERRUPT* = 9 # Operation terminated by sqlite3_interrupt() + SQLITE_IOERR* = 10 # Some kind of disk I/O error occurred + SQLITE_CORRUPT* = 11 # The database disk image is malformed + SQLITE_NOTFOUND* = 12 # (Internal Only) Table or record not found + SQLITE_FULL* = 13 # Insertion failed because database is full + SQLITE_CANTOPEN* = 14 # Unable to open the database file + SQLITE_PROTOCOL* = 15 # Database lock protocol error + SQLITE_EMPTY* = 16 # Database is empty + SQLITE_SCHEMA* = 17 # The database schema changed + SQLITE_TOOBIG* = 18 # Too much data for one row of a table + SQLITE_CONSTRAINT* = 19 # Abort due to constraint violation + SQLITE_MISMATCH* = 20 # Data type mismatch + SQLITE_MISUSE* = 21 # Library used incorrectly + SQLITE_NOLFS* = 22 # Uses OS features not supported on host + SQLITE_AUTH* = 23 # Authorization denied + SQLITE_FORMAT* = 24 # Auxiliary database format error + SQLITE_RANGE* = 25 # 2nd parameter to sqlite3_bind out of range + SQLITE_NOTADB* = 26 # File opened that is not a database file + SQLITE_ROW* = 100 # sqlite3_step() has another row ready + SQLITE_DONE* = 101 # sqlite3_step() has finished executing + SQLITE_COPY* = 0 + SQLITE_CREATE_INDEX* = 1 + SQLITE_CREATE_TABLE* = 2 + SQLITE_CREATE_TEMP_INDEX* = 3 + SQLITE_CREATE_TEMP_TABLE* = 4 + SQLITE_CREATE_TEMP_TRIGGER* = 5 + SQLITE_CREATE_TEMP_VIEW* = 6 + SQLITE_CREATE_TRIGGER* = 7 + SQLITE_CREATE_VIEW* = 8 + SQLITE_DELETE* = 9 + SQLITE_DROP_INDEX* = 10 + SQLITE_DROP_TABLE* = 11 + SQLITE_DROP_TEMP_INDEX* = 12 + SQLITE_DROP_TEMP_TABLE* = 13 + SQLITE_DROP_TEMP_TRIGGER* = 14 + SQLITE_DROP_TEMP_VIEW* = 15 + SQLITE_DROP_TRIGGER* = 16 + SQLITE_DROP_VIEW* = 17 + SQLITE_INSERT* = 18 + SQLITE_PRAGMA* = 19 + SQLITE_READ* = 20 + SQLITE_SELECT* = 21 + SQLITE_TRANSACTION* = 22 + SQLITE_UPDATE* = 23 + SQLITE_ATTACH* = 24 + SQLITE_DETACH* = 25 + SQLITE_ALTER_TABLE* = 26 + SQLITE_REINDEX* = 27 + SQLITE_DENY* = 1 + SQLITE_IGNORE* = 2 # Original from sqlite3.h: + #define SQLITE_STATIC ((void(*)(void *))0) + #define SQLITE_TRANSIENT ((void(*)(void *))-1) + SQLITE_DETERMINISTIC* = 0x800 + +type + Sqlite3 {.pure, final.} = object + PSqlite3* = ptr Sqlite3 + PPSqlite3* = ptr PSqlite3 + Sqlite3_Backup {.pure, final.} = object + PSqlite3_Backup* = ptr Sqlite3_Backup + PPSqlite3_Backup* = ptr PSqlite3_Backup + Context{.pure, final.} = object + Pcontext* = ptr Context + TStmt{.pure, final.} = object + PStmt* = ptr TStmt + InstantRow* = PStmt ## A handle that can be used to get a row's column + Value{.pure, final.} = object + PValue* = ptr Value + PValueArg* = array[0..127, PValue] + + Callback* = proc (para1: pointer, para2: int32, para3, + para4: cstringArray): int32{.cdecl.} + Tbind_destructor_func* = proc (para1: pointer){.cdecl, tags: [], gcsafe.} + Create_function_step_func* = proc (para1: Pcontext, para2: int32, + para3: PValueArg){.cdecl.} + Create_function_func_func* = proc (para1: Pcontext, para2: int32, + para3: PValueArg){.cdecl.} + Create_function_final_func* = proc (para1: Pcontext){.cdecl.} + Result_func* = proc (para1: pointer){.cdecl.} + Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer, + para4: int32, para5: pointer): int32{.cdecl.} + Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32, + para4: cstring){.cdecl.} + +const + SQLITE_STATIC* = nil + SQLITE_TRANSIENT* = cast[Tbind_destructor_func](-1) + +proc close*(para1: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_close".} +proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer, + errmsg: var cstring): int32{.cdecl, mylib, + importc: "sqlite3_exec".} +proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, mylib, + importc: "sqlite3_last_insert_rowid".} +proc changes*(para1: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_changes".} +proc total_changes*(para1: PSqlite3): int32{.cdecl, mylib, + importc: "sqlite3_total_changes".} +proc interrupt*(para1: PSqlite3){.cdecl, mylib, importc: "sqlite3_interrupt".} +proc complete*(sql: cstring): int32{.cdecl, mylib, + importc: "sqlite3_complete".} +proc complete16*(sql: pointer): int32{.cdecl, mylib, + importc: "sqlite3_complete16".} +proc busy_handler*(para1: PSqlite3, + para2: proc (para1: pointer, para2: int32): int32{.cdecl.}, + para3: pointer): int32{.cdecl, mylib, + importc: "sqlite3_busy_handler".} +proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, mylib, + importc: "sqlite3_busy_timeout".} +proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray, + nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl, + mylib, importc: "sqlite3_get_table".} +proc free_table*(result: cstringArray){.cdecl, mylib, + importc: "sqlite3_free_table".} + # Todo: see how translate sqlite3_mprintf, sqlite3_vmprintf, sqlite3_snprintf + # function sqlite3_mprintf(_para1:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_mprintf'; +proc mprintf*(para1: cstring): cstring{.cdecl, varargs, mylib, + importc: "sqlite3_mprintf".} + #function sqlite3_vmprintf(_para1:Pchar; _para2:va_list):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_vmprintf'; +proc free*(z: cstring){.cdecl, mylib, importc: "sqlite3_free".} + #function sqlite3_snprintf(_para1:longint; _para2:Pchar; _para3:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_snprintf'; +proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl, + mylib, varargs, importc: "sqlite3_snprintf".} +proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32, + para3: cstring, para4: cstring, para5: cstring, para6: cstring): int32{. + cdecl.}, pUserData: pointer): int32{.cdecl, mylib, + importc: "sqlite3_set_authorizer".} +proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.}, + para3: pointer): pointer{.cdecl, mylib, + importc: "sqlite3_trace".} +proc progress_handler*(para1: PSqlite3, para2: int32, + para3: proc (para1: pointer): int32{.cdecl.}, + para4: pointer){.cdecl, mylib, + importc: "sqlite3_progress_handler".} +proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.}, + para3: pointer): pointer{.cdecl, mylib, + importc: "sqlite3_commit_hook".} +proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, mylib, + importc: "sqlite3_open".} +proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, mylib, + importc: "sqlite3_open16".} +proc errcode*(db: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_errcode".} +proc errmsg*(para1: PSqlite3): cstring{.cdecl, mylib, importc: "sqlite3_errmsg".} +proc errmsg16*(para1: PSqlite3): pointer{.cdecl, mylib, + importc: "sqlite3_errmsg16".} +proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var PStmt, + pzTail: ptr cstring): int32{.cdecl, mylib, + importc: "sqlite3_prepare".} + +proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var PStmt, + pzTail: ptr cstring): cint {. + importc: "sqlite3_prepare_v2", cdecl, mylib.} + +proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var PStmt, + pzTail: var pointer): int32{.cdecl, mylib, + importc: "sqlite3_prepare16".} +proc bind_blob*(para1: PStmt, para2: int32, para3: pointer, n: int32, + para5: Tbind_destructor_func): int32{.cdecl, mylib, + importc: "sqlite3_bind_blob".} +proc bind_double*(para1: PStmt, para2: int32, para3: float64): int32{.cdecl, + mylib, importc: "sqlite3_bind_double".} +proc bind_int*(para1: PStmt, para2: int32, para3: int32): int32{.cdecl, + mylib, importc: "sqlite3_bind_int".} +proc bind_int64*(para1: PStmt, para2: int32, para3: int64): int32{.cdecl, + mylib, importc: "sqlite3_bind_int64".} +proc bind_null*(para1: PStmt, para2: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_null".} +proc bind_text*(para1: PStmt, para2: int32, para3: cstring, n: int32, + para5: Tbind_destructor_func): int32{.cdecl, mylib, + importc: "sqlite3_bind_text".} +proc bind_text16*(para1: PStmt, para2: int32, para3: pointer, para4: int32, + para5: Tbind_destructor_func): int32{.cdecl, mylib, + importc: "sqlite3_bind_text16".} + #function sqlite3_bind_value(_para1:Psqlite3_stmt; _para2:longint; _para3:Psqlite3_value):longint;cdecl; external Sqlite3Lib name 'sqlite3_bind_value'; + #These overloaded functions were introduced to allow the use of SQLITE_STATIC and SQLITE_TRANSIENT + #It's the c world man ;-) +proc bind_blob*(para1: PStmt, para2: int32, para3: pointer, n: int32, + para5: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_blob".} +proc bind_text*(para1: PStmt, para2: int32, para3: cstring, n: int32, + para5: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_text".} +proc bind_text16*(para1: PStmt, para2: int32, para3: pointer, para4: int32, + para5: int32): int32{.cdecl, mylib, + importc: "sqlite3_bind_text16".} +proc bind_parameter_count*(para1: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_bind_parameter_count".} +proc bind_parameter_name*(para1: PStmt, para2: int32): cstring{.cdecl, + mylib, importc: "sqlite3_bind_parameter_name".} +proc bind_parameter_index*(para1: PStmt, zName: cstring): int32{.cdecl, + mylib, importc: "sqlite3_bind_parameter_index".} +proc clear_bindings*(para1: PStmt): int32 {.cdecl, + mylib, importc: "sqlite3_clear_bindings".} +proc column_count*(PStmt: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_column_count".} +proc column_name*(para1: PStmt, para2: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_name".} +proc column_table_name*(para1: PStmt; para2: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_table_name".} +proc column_name16*(para1: PStmt, para2: int32): pointer{.cdecl, mylib, + importc: "sqlite3_column_name16".} +proc column_decltype*(para1: PStmt, i: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_decltype".} +proc column_decltype16*(para1: PStmt, para2: int32): pointer{.cdecl, + mylib, importc: "sqlite3_column_decltype16".} +proc step*(para1: PStmt): int32{.cdecl, mylib, importc: "sqlite3_step".} +proc data_count*(PStmt: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_data_count".} +proc column_blob*(para1: PStmt, iCol: int32): pointer{.cdecl, mylib, + importc: "sqlite3_column_blob".} +proc column_bytes*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_bytes".} +proc column_bytes16*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_bytes16".} +proc column_double*(para1: PStmt, iCol: int32): float64{.cdecl, mylib, + importc: "sqlite3_column_double".} +proc column_int*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_int".} +proc column_int64*(para1: PStmt, iCol: int32): int64{.cdecl, mylib, + importc: "sqlite3_column_int64".} +proc column_text*(para1: PStmt, iCol: int32): cstring{.cdecl, mylib, + importc: "sqlite3_column_text".} +proc column_text16*(para1: PStmt, iCol: int32): pointer{.cdecl, mylib, + importc: "sqlite3_column_text16".} +proc column_type*(para1: PStmt, iCol: int32): int32{.cdecl, mylib, + importc: "sqlite3_column_type".} +proc finalize*(PStmt: PStmt): int32{.cdecl, mylib, + importc: "sqlite3_finalize".} +proc reset*(PStmt: PStmt): int32{.cdecl, mylib, importc: "sqlite3_reset".} +proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32, + eTextRep: int32, para5: pointer, + xFunc: Create_function_func_func, + xStep: Create_function_step_func, + xFinal: Create_function_final_func): int32{.cdecl, + mylib, importc: "sqlite3_create_function".} +proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32, + eTextRep: int32, para5: pointer, + xFunc: Create_function_func_func, + xStep: Create_function_step_func, + xFinal: Create_function_final_func): int32{.cdecl, + mylib, importc: "sqlite3_create_function16".} +proc aggregate_count*(para1: Pcontext): int32{.cdecl, mylib, + importc: "sqlite3_aggregate_count".} +proc value_blob*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_blob".} +proc value_bytes*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_bytes".} +proc value_bytes16*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_bytes16".} +proc value_double*(para1: PValue): float64{.cdecl, mylib, + importc: "sqlite3_value_double".} +proc value_int*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_int".} +proc value_int64*(para1: PValue): int64{.cdecl, mylib, + importc: "sqlite3_value_int64".} +proc value_text*(para1: PValue): cstring{.cdecl, mylib, + importc: "sqlite3_value_text".} +proc value_text16*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_text16".} +proc value_text16le*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_text16le".} +proc value_text16be*(para1: PValue): pointer{.cdecl, mylib, + importc: "sqlite3_value_text16be".} +proc value_type*(para1: PValue): int32{.cdecl, mylib, + importc: "sqlite3_value_type".} +proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl, + mylib, importc: "sqlite3_aggregate_context".} +proc user_data*(para1: Pcontext): pointer{.cdecl, mylib, + importc: "sqlite3_user_data".} +proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, mylib, + importc: "sqlite3_get_auxdata".} +proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer, + para4: proc (para1: pointer){.cdecl.}){.cdecl, mylib, + importc: "sqlite3_set_auxdata".} +proc result_blob*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_blob".} +proc result_double*(para1: Pcontext, para2: float64){.cdecl, mylib, + importc: "sqlite3_result_double".} +proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl, + mylib, importc: "sqlite3_result_error".} +proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl, + mylib, importc: "sqlite3_result_error16".} +proc result_int*(para1: Pcontext, para2: int32){.cdecl, mylib, + importc: "sqlite3_result_int".} +proc result_int64*(para1: Pcontext, para2: int64){.cdecl, mylib, + importc: "sqlite3_result_int64".} +proc result_null*(para1: Pcontext){.cdecl, mylib, + importc: "sqlite3_result_null".} +proc result_text*(para1: Pcontext, para2: cstring, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text".} +proc result_text16*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text16".} +proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text16le".} +proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32, + para4: Result_func){.cdecl, mylib, + importc: "sqlite3_result_text16be".} +proc result_value*(para1: Pcontext, para2: PValue){.cdecl, mylib, + importc: "sqlite3_result_value".} +proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32, + para4: pointer, xCompare: Create_collation_func): int32{. + cdecl, mylib, importc: "sqlite3_create_collation".} +proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32, + para4: pointer, xCompare: Create_collation_func): int32{. + cdecl, mylib, importc: "sqlite3_create_collation16".} +proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{. + cdecl, mylib, importc: "sqlite3_collation_needed".} +proc collation_needed16*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{. + cdecl, mylib, importc: "sqlite3_collation_needed16".} +proc libversion*(): cstring{.cdecl, mylib, importc: "sqlite3_libversion".} + #Alias for allowing better code portability (win32 is not working with external variables) +proc version*(): cstring{.cdecl, mylib, importc: "sqlite3_libversion".} + # Not published functions +proc libversion_number*(): int32{.cdecl, mylib, + importc: "sqlite3_libversion_number".} + +proc backup_init*(pDest: PSqlite3, zDestName: cstring, pSource: PSqlite3, zSourceName: cstring): PSqlite3_Backup {. + cdecl, mylib, importc: "sqlite3_backup_init".} + +proc backup_step*(pBackup: PSqlite3_Backup, nPage: int32): int32 {.cdecl, mylib, importc: "sqlite3_backup_step".} + +proc backup_finish*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_finish".} + +proc backup_pagecount*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_pagecount".} + +proc backup_remaining*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_remaining".} + +proc sqlite3_sleep*(t: int64): int64 {.cdecl, mylib, importc: "sqlite3_sleep".} + + #function sqlite3_key(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_key'; + #function sqlite3_rekey(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_rekey'; + #function sqlite3_sleep(_para1:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_sleep'; + #function sqlite3_expired(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_expired'; + #function sqlite3_global_recover:longint;cdecl; external Sqlite3Lib name 'sqlite3_global_recover'; +# implementation + +when defined(nimHasStyleChecks): + {.pop.} diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_impl.nim b/clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_impl.nim new file mode 100644 index 0000000..264084d --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_impl.nim @@ -0,0 +1,148 @@ +import std/asyncdispatch +import std/httpclient +import std/strformat +import std/strutils +import std/json +import ../../error +import ./surreal_rdb +import ./surreal_lib + +type SurrealImpl* = ref object + + +proc surrealTimeoutMs(timeout: int): int = + if timeout <= 0: + return 0 + if timeout > high(int) div 1000: + return high(int) + result = timeout * 1000 + + +proc awaitWithTimeout[T](fut: Future[T], timeoutMs: int, timeoutMsg: string): Future[T] {.async.} = + if timeoutMs <= 0: + return await fut + + let ok = await withTimeout(fut, timeoutMs) + if not ok: + dbError(timeoutMsg) + result = fut.read + + +proc failSurrealBody(body: JsonNode) = + if body.kind == JObject: + if body.hasKey("code") and body["code"].kind == JInt and body["code"].getInt == 400: + if body.hasKey("information") and body["information"].kind == JString: + dbError(body["information"].getStr()) + if body.hasKey("detail") and not body.hasKey("result") and body["detail"].kind == JString: + dbError(body["detail"].getStr()) + + if body.kind == JArray: + for row in body.items: + if row.kind != JObject: + continue + if row.hasKey("status") and row["status"].kind == JString and row["status"].getStr() == "ERR": + if row.hasKey("detail") and row["detail"].kind == JString: + dbError(row["detail"].getStr()) + if row.hasKey("detail") and not row.hasKey("result") and row["detail"].kind == JString: + dbError(row["detail"].getStr()) + + +proc runSurrealSql(db: SurrealConn, sql: string, timeout: int): Future[JsonNode] {.async.} = + let timeoutMs = surrealTimeoutMs(timeout) + let url = &"{db.host}:{db.port}/sql" + let response = await awaitWithTimeout( + db.client.post(url, sql), + timeoutMs, + &"SurrealDb request timed out after {timeoutMs} ms" + ) + + let bodyText = await awaitWithTimeout(response.body(), timeoutMs, + &"SurrealDb response body timed out after {timeoutMs} ms") + + var body: JsonNode + try: + body = bodyText.parseJson() + except CatchableError: + dbError(&"SurrealDb returned non-JSON response: {bodyText}") + + failSurrealBody(body) + if response.status != $Http200: + dbError(&"SurrealDb request failed with HTTP status {response.status}") + + return body + +# proc open*(_:type SurrealImpl, namespace="", database="",user="", password="", +# host="", port:int32 = 0, maxConnections=1, timeout=30):Future[SurrealConnections] {.async.} = +# var pools = newSeq[SurrealConn](maxConnections) +# for i in 0..= s.high: + return false + if s.find(':', colonPos + 1) >= 0: + return false + if s.contains(' ') or s.contains('"') or s.contains('\''): + return false + if not (s[0].isAlphaAscii or s[0] == '_'): + return false + for ch in s[1 ..< colonPos]: + if not (ch.isAlphaNumeric or ch == '_'): + return false + for ch in s[colonPos + 1 .. ^1]: + if ch.isSpaceAscii: + return false + return true + + +proc looksLikeDateTimeValue(s: string): bool = + if s.len < 10: + return false + + for idx in [0, 1, 2, 3, 5, 6, 8, 9]: + if idx >= s.len or not s[idx].isDigit: + return false + + if s[4] != '-' or s[7] != '-': + return false + + if s.len == 10: + return true + + if s.len < 19: + return false + + if s[10] notin {'T', ' '}: + return false + + for idx in [11, 12, 14, 15, 17, 18]: + if idx >= s.len or not s[idx].isDigit: + return false + + if s[13] != ':' or s[16] != ':': + return false + + if s.len == 19: + return true + + return s[19] in {'.', 'Z', '+', '-'} + + +proc dbQuote(s:string):string = + ## DB quotes the string. + if s == "null": + return "NULL" + if looksLikeRecordId(s): + return s + result = newStringOfCap(s.len + 2) + result.add "'" + for c in items(s): + # see https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html#mysql-escaping + case c + of '\0': result.add "\\0" + of '\b': result.add "\\b" + of '\t': result.add "\\t" + of '\l': result.add "\\n" + of '\r': result.add "\\r" + of '\x1a': result.add "\\Z" + of '"': result.add "\\\"" + of '\'': result.add "\\'" + of '\\': result.add "\\\\" + of '_': result.add "\\_" + else: result.add c + add(result, '\'') + + +proc appendAlphabet(result: var string, n: int) = + var n = n + if n <= 0: + return + + var chars: seq[char] = @[] + while n > 0: + n.dec() + chars.add(chr(ord('a') + (n mod 26))) + n = n div 26 + + for i in countdown(chars.high, 0): + result.add(chars[i]) + + +proc dbFormat*(formatstr: string, args: varargs[string]): string = + var a = 0 + result = newStringOfCap(formatstr.len + args.len * 8) + var segStart = 0 + for j in 0 ..< formatstr.len: + if formatstr[j] == '?': + if j > segStart: + result.add(formatstr[segStart ..< j]) + result.add(dbQuote(args[a])) + inc(a) + segStart = j + 1 + if segStart < formatstr.len: + result.add(formatstr[segStart ..< formatstr.len]) + + +proc numToAlphabet*(n:int):string = + ## 1 => "a", 26 => "z", 27 => "aa", 28 => "ab", 52 => "az", 53 => "ba" + result = newStringOfCap(8) + appendAlphabet(result, n) + + +proc questionToDaller*(s: string): string = + ## from `UPDATE user SET name = ?, email = ? WHERE id = ?` + ## + ## to `UPDATE user SET name = $a, email = $b WHERE id = $c` + var i = 1 + var segStart = 0 + result = newStringOfCap(s.len + 8) + for j in 0 ..< s.len: + if s[j] == '?': + if j > segStart: + result.add(s[segStart ..< j]) + result.add('$') + appendAlphabet(result, i) + inc(i) + segStart = j + 1 + if segStart < s.len: + result.add(s[segStart ..< s.len]) + + +proc appendJsonLetClause(result: var string, idx: int, arg: JsonNode, quoteString: bool) = + result.add("LET $") + appendAlphabet(result, idx) + result.add(" = ") + case arg.kind + of JBool: + result.add($arg.getBool) + of JInt: + result.add($arg.getInt) + of JFloat: + result.add($arg.getFloat) + of JString: + let val = arg.getStr().replace("\"", "\\\"") + if looksLikeRecordId(val): + result.add(val) + elif looksLikeDateTimeValue(val): + result.add("\"") + result.add(val) + result.add("\"") + elif quoteString: + result.add('"') + result.add(val) + result.add('"') + else: + result.add(val) + of JNull: + result.add("NONE") + of JArray, JObject: + result.add($arg) + result.add("; ") + + +proc dbFormatPrepared*(normalizedQueryString: string, args: JsonNode): string = + ## `normalizedQueryString` should already have `?` converted to SurrealQL + ## style placeholders. + if args.isNil: + result = newStringOfCap(normalizedQueryString.len) + result.add(normalizedQueryString) + return + if args.kind == JNull: + result = newStringOfCap(normalizedQueryString.len) + result.add(normalizedQueryString) + return + + result = newStringOfCap(normalizedQueryString.len + max(args.len, 1) * 24) + if args.kind == JArray and args.len > 0: + var i = 1 + for arg in args.items: + appendJsonLetClause(result, i, arg, true) + inc(i) + elif args.kind == JObject and args.len > 0: + var i = 1 + for (_, arg) in args.pairs: + appendJsonLetClause(result, i, arg, false) + inc(i) + + result.add(normalizedQueryString) + + +proc dbFormat*(queryString: string, args: JsonNode): string = + result = dbFormatPrepared(queryString.questionToDaller(), args) diff --git a/clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_rdb.nim b/clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_rdb.nim new file mode 100644 index 0000000..1454912 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/libs/surreal/surreal_rdb.nim @@ -0,0 +1,6 @@ +import std/httpclient + +type SurrealConn* = object + client*: AsyncHttpClient + host*:string + port*:int32 diff --git a/clients/nim-allographer/src/allographer/query_builder/log.nim b/clients/nim-allographer/src/allographer/query_builder/log.nim new file mode 100644 index 0000000..f93a4eb --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/log.nim @@ -0,0 +1,58 @@ +import std/os +import std/parsecfg +import std/terminal +import std/logging +import std/macros +import std/strformat + + +type + LogSetting* = ref object + shouldDisplayLog*: bool + shouldOutputLogFile*: bool + logDir*: string + +proc logger*(self:LogSetting, output: auto) = + # let msg = $output & " " & $args + let msg = $output + # console log + if self.shouldDisplayLog: + let logger = newConsoleLogger() + logger.log(lvlDebug, msg) + # file log + if self.shouldOutputLogFile: + # info $output & $args + let path = self.logDir & "/log.log" + createDir(parentDir(path)) + let logger = newRollingFileLogger(path, mode=fmAppend, fmtStr=verboseFmtStr) + defer: logger.file.close() + logger.log(lvlDebug, msg) + flushFile(logger.file) + + +proc echoErrorMsg*(self:LogSetting, msg:string, args:seq[string] = @[]) = + let msg = msg & " " & $args + # console log + if self.shouldDisplayLog: + styledWriteLine(stdout, fgRed, bgDefault, msg, resetStyle) + # file log + if self.shouldOutputLogFile: + let path = self.logDir & "/error.log" + createDir(parentDir(path)) + let logger = newRollingFileLogger(path, mode=fmAppend, fmtStr=verboseFmtStr) + defer: logger.file.close() + logger.log(lvlError, msg) + flushFile(logger.file) + +proc echoWarningMsg*(self:LogSetting, msg:string) = + # console log + if self.shouldDisplayLog: + styledWriteLine(stdout, fgYellow, bgDefault, msg, resetStyle) + # file log + if self.shouldOutputLogFile: + let path = self.logDir & "/error.log" + createDir(parentDir(path)) + let logger = newRollingFileLogger(path, mode=fmAppend, fmtStr=verboseFmtStr) + defer: logger.file.close() + logger.log(lvlError, msg) + flushFile(logger.file) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_exec.nim b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_exec.nim new file mode 100644 index 0000000..be435c9 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_exec.nim @@ -0,0 +1,716 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/monotimes +import std/options +import std/strformat +import std/strutils +import std/sequtils +import std/tables +import std/times +import ../../error +import ../../libs/baradb/baradb_client +import ../../log +import ../database_types +import ./query/baradb_builder +import ./baradb_types + + +# ================================================================================ +# connection pool +# ================================================================================ + +proc removePoolWaiter(pools: Connections, w: Future[void]) = + var kept = initDeque[Future[void]]() + while pools.waiters.len > 0: + let x = pools.waiters.popFirst() + if x != w: + kept.addLast(x) + pools.waiters = move(kept) + +proc wakeOnePoolWaiter(pools: Connections) = + while pools.waiters.len > 0: + let w = pools.waiters.popFirst() + if w.finished: + continue + w.complete() + break + +proc poolRemainingMs(deadline: MonoTime): int = + let left = (deadline - getMonoTime()).inMilliseconds + if left <= 0: + return 0 + if left > int64(high(int)): + return high(int) + result = int(left) + if result < 1: + result = 1 + +proc nowUnix(): int64 = + getTime().toUnix() + +proc hasConnExpired(self: Connections, conn: Connection): bool = + let now = nowUnix() + if self.maxConnectionLifetime > 0 and now - conn.createdAt >= self.maxConnectionLifetime.int64: + return true + if self.maxConnectionIdleTime > 0 and now - conn.lastUsedAt >= self.maxConnectionIdleTime.int64: + return true + return false + +proc openBaradbConn(self: Connections): BaraClient = + let config = ClientConfig( + host: self.host, + port: self.port, + database: self.database, + username: self.user, + password: self.password, + timeoutMs: self.timeout * 1000, + maxRetries: 3, + ) + let client = newClient(config) + waitFor client.connect() + return client + +proc refreshConn(self: Connections, connI: int): bool = + if connI < 0 or connI >= self.conns.len: + return false + let client = openBaradbConn(self) + let oldConn = self.conns[connI].client + if not oldConn.isNil: + oldConn.close() + self.conns[connI].client = client + self.conns[connI].createdAt = nowUnix() + self.conns[connI].lastUsedAt = self.conns[connI].createdAt + return true + +proc getFreeConn(self: BaradbConnections | BaradbQuery | RawBaradbQuery): Future[int] {.async.} = + let deadline = getMonoTime() + initDuration(seconds = self.pools.timeout) + while true: + for i in 0 ..< self.pools.conns.len: + if not self.pools.conns[i].isBusy: + self.pools.conns[i].isBusy = true + if self.pools.hasConnExpired(self.pools.conns[i]): + try: + discard self.pools.refreshConn(i) + except CatchableError: + discard + when defined(check_pool): + echo "=== getFreeConn ", i + return i + if getMonoTime() >= deadline: + return errorConnectionNum + let w = newFuture[void]("getFreeConn.poolWait") + self.pools.waiters.addLast(w) + var ms = poolRemainingMs(deadline) + if ms < 1: + ms = 1 + let ok = await withTimeout(w, ms) + if not ok: + removePoolWaiter(self.pools, w) + return errorConnectionNum + + +proc returnConn(self: BaradbConnections | BaradbQuery | RawBaradbQuery, i: int) {.async.} = + if i != errorConnectionNum: + self.pools.conns[i].isBusy = false + self.pools.conns[i].lastUsedAt = nowUnix() + wakeOnePoolWaiter(self.pools) + + +proc raisePoolTimeout(self: BaradbConnections | BaradbQuery | RawBaradbQuery) {.noreturn.} = + raise newException(DbError, "Timed out while waiting for a free Baradb connection") + + +# ================================================================================ +# SQL formatting helpers +# ================================================================================ + +proc escapeSqlValue(val: JsonNode): string = + case val.kind + of JNull: + return "NULL" + of JBool: + return if val.getBool: "TRUE" else: "FALSE" + of JInt: + return $val.getInt + of JFloat: + return $val.getFloat + of JString: + let s = val.getStr + return "'" & s.replace("'", "''") & "'" + else: + return "'" & val.pretty.replace("'", "''") & "'" + + +proc formatSql*(sql: string, args: seq[JsonNode]): string = + result = sql + for arg in args: + let pos = result.find("?") + if pos < 0: + break + result = result[0.. 0 and qr.rows[0].len > 0: + return qr.rows[0][0] + return "" + + +proc getColumns(self: BaradbQuery, queryString: string, args = newJArray()): Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var arr: seq[JsonNode] + for arg in args.items: + arr.add(arg["value"]) + let sql = formatSql(queryString, arr) + let qr = await self.pools.conns[connI].client.query(sql) + if qr.rowCount > 0: + return qr.rows[0] + return @[] + + +proc transactionStart(self: BaradbConnections) {.async.} = + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + self.isInTransaction = true + self.transactionConn = connI + discard await self.pools.conns[connI].client.exec("BEGIN") + + +proc transactionEnd(self: BaradbConnections, query: string) {.async.} = + defer: + self.returnConn(self.transactionConn).await + self.transactionConn = 0 + self.isInTransaction = false + + discard await self.pools.conns[self.transactionConn].client.exec(query) + + +# ================================================================================ +# public exec +# ================================================================================ + +# ==================== return json ==================== +proc get*(self: BaradbQuery): Future[seq[JsonNode]] {.async.} = + let sql = self.selectBuilder() + try: + self.log.logger(sql) + return await self.getAllRows(sql) + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc first*(self: BaradbQuery): Future[Option[JsonNode]] {.async.} = + let sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return await self.getRow(sql) + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc find*(self: BaradbQuery, id: string, key = "id"): Future[Option[JsonNode]] {.async.} = + self.placeHolder.add(%*{"key": key, "value": id}) + let sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return await self.getRow(sql) + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc find*(self: BaradbQuery, id: int, key = "id"): Future[Option[JsonNode]] {.async.} = + return await self.find($id, key) + + +# ==================== return string ==================== +proc getPlain*(self: BaradbQuery): Future[seq[seq[string]]] {.async.} = + let sql = self.selectBuilder() + try: + self.log.logger(sql) + return await self.getAllRowsPlain(sql, self.placeHolder) + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc firstPlain*(self: BaradbQuery): Future[seq[string]] {.async.} = + let sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return await self.getRowPlain(sql, self.placeHolder) + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc findPlain*(self: BaradbQuery, id: string, key = "id"): Future[seq[string]] {.async.} = + self.placeHolder.add(%*{"key": key, "value": id}) + let sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return await self.getRowPlain(sql, self.placeHolder) + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc findPlain*(self: BaradbQuery, id: int, key = "id"): Future[seq[string]] {.async.} = + return await self.findPlain($id, key) + + +# ==================== insert JsonNode ==================== +proc insert*(self: BaradbQuery, items: JsonNode) {.async.} = + let sql = self.insertValueBuilder(items) + self.log.logger(sql) + await self.exec(sql) + + +proc insert*(self: BaradbQuery, items: seq[JsonNode]) {.async.} = + let sql = self.insertValuesBuilder(items) + self.log.logger(sql) + await self.exec(sql) + + +proc insertId*(self: BaradbQuery, items: JsonNode, key = "id"): Future[string] {.async.} = + let sql = self.insertValueBuilder(items) + self.log.logger(sql) + return await self.insertId(sql, key) + + +proc insertId*(self: BaradbQuery, items: seq[JsonNode], key = "id"): Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + let sql = self.insertValueBuilder(item) + self.log.logger(sql) + result[i] = await self.insertId(sql, key) + self.placeHolder = newJArray() + + +# ==================== insert Object ==================== +proc insert*[T](self: BaradbQuery, items: T) {.async.} = + let sql = self.insertValueBuilder(%items) + self.log.logger(sql) + await self.exec(sql) + + +proc insert*[T](self: BaradbQuery, items: seq[T]) {.async.} = + let items = items.mapIt(%it) + let sql = self.insertValuesBuilder(items) + self.log.logger(sql) + await self.exec(sql) + + +proc insertId*[T](self: BaradbQuery, items: T, key = "id"): Future[string] {.async.} = + let sql = self.insertValueBuilder(%items) + self.log.logger(sql) + return await self.insertId(sql, key) + + +proc insertId*[T](self: BaradbQuery, items: seq[T], key = "id"): Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + let sql = self.insertValueBuilder(%item) + self.log.logger(sql) + result[i] = await self.insertId(sql, key) + self.placeHolder = newJArray() + + +# ==================== update ==================== +proc update*(self: BaradbQuery, items: JsonNode) {.async.} = + let sql = self.updateBuilder(items) + self.log.logger(sql) + await self.exec(sql) + + +proc update*[T](self: BaradbQuery, items: T) {.async.} = + let sql = self.updateBuilder(%items) + self.log.logger(sql) + await self.exec(sql) + + +proc delete*(self: BaradbQuery) {.async.} = + let sql = self.deleteBuilder() + self.log.logger(sql) + await self.exec(sql) + + +proc delete*(self: BaradbQuery, id: int, key = "id") {.async.} = + let sql = self.deleteByIdBuilder(id, key) + self.log.logger(sql) + self.placeHolder.add(%*{"key": key, "value": id}) + await self.exec(sql) + + +proc columns*(self: BaradbQuery): Future[seq[string]] {.async.} = + let sql = self.columnBuilder() + self.log.logger(sql) + return await self.getColumns(sql, self.placeHolder) + + +proc count*(self: BaradbQuery): Future[int] {.async.} = + let sql = self.countBuilder() + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getStr().parseInt() + else: + return 0 + + +proc min*(self: BaradbQuery, column: string): Future[Option[string]] {.async.} = + let sql = self.minBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc max*(self: BaradbQuery, column: string): Future[Option[string]] {.async.} = + let sql = self.maxBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc avg*(self: BaradbQuery, column: string): Future[Option[float]] {.async.} = + let sql = self.avgBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getStr().parseFloat.some + else: + return none(float) + + +proc sum*(self: BaradbQuery, column: string): Future[Option[float]] {.async.} = + let sql = self.sumBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getStr.parseFloat.some + else: + return none(float) + + +proc begin*(self: BaradbConnections) {.async.} = + self.log.logger("BEGIN") + await self.transactionStart() + + +proc rollback*(self: BaradbConnections) {.async.} = + self.log.logger("ROLLBACK") + await self.transactionEnd("ROLLBACK") + + +proc commit*(self: BaradbConnections) {.async.} = + self.log.logger("COMMIT") + await self.transactionEnd("COMMIT") + + +# raw queries +proc get*(self: RawBaradbQuery): Future[seq[JsonNode]] {.async.} = + self.log.logger(self.queryString) + return await self.getAllRows(self.queryString) + + +proc getPlain*(self: RawBaradbQuery): Future[seq[seq[string]]] {.async.} = + self.log.logger(self.queryString) + return await self.getAllRowsPlain(self.queryString, self.placeHolder) + + +proc exec*(self: RawBaradbQuery) {.async.} = + self.log.logger(self.queryString) + await self.exec(self.queryString, self.placeHolder) + + +proc first*(self: RawBaradbQuery): Future[Option[JsonNode]] {.async.} = + self.log.logger(self.queryString) + return await self.getRow(self.queryString) + + +proc firstPlain*(self: RawBaradbQuery): Future[seq[string]] {.async.} = + self.log.logger(self.queryString) + return await self.getRowPlain(self.queryString, self.placeHolder) + + +# seeder templates +template seeder*(rdb: BaradbConnections, tableName: string, body: untyped): untyped = + block: + if waitFor rdb.table(tableName).count() == 0: + body + + +template seeder*(rdb: BaradbConnections, tableName, column: string, body: untyped): untyped = + block: + if waitFor rdb.select(column).table(tableName).count() == 0: + body diff --git a/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_open.nim b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_open.nim new file mode 100644 index 0000000..8f517b1 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_open.nim @@ -0,0 +1,52 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/tables +import std/times +import ../../libs/baradb/baradb_client +import ../../log +import ./baradb_types + + +proc dbOpen*(_: type Baradb, database: string, user: string, password: string, + host: string, port: int, maxConnections = 1, timeout = 30, + shouldDisplayLog = false, shouldOutputLogFile = false, logDir = "", + maxConnectionLifetime = DEFAULT_CONN_MAX_LIFETIME_SECONDS, + maxConnectionIdleTime = DEFAULT_CONN_MAX_IDLE_SECONDS): BaradbConnections = + var conns = newSeq[Connection](maxConnections) + for i in 0..=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"] +const whereSymbolsError = """Arg position 3 is only allowed of ["is", "is not", "=", "!=", "<", "<=", ">=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"]""" + +proc where*(self: BaradbQuery, column: string, symbol: string, + value: string|int|float): BaradbQuery = + if not whereSymbols.contains(symbol): + raise newException(CatchableError, whereSymbolsError) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "?" + }) + return self + + +proc where*(self: BaradbQuery, column: string, symbol: string, + value: bool): BaradbQuery = + if not whereSymbols.contains(symbol): + raise newException(CatchableError, whereSymbolsError) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "?" + }) + return self + + +proc where*(self: BaradbQuery, column: string, symbol: string, value: nil.type): BaradbQuery = + if not whereSymbols.contains(symbol): + raise newException(CatchableError, whereSymbolsError) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "null" + }) + return self + + +proc orWhere*(self: BaradbQuery, column: string, symbol: string, + value: string|int|float|bool): BaradbQuery = + if not whereSymbols.contains(symbol): + raise newException(CatchableError, whereSymbolsError) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["or_where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "?" + }) + return self + + +proc orWhere*(self: BaradbQuery, column: string, symbol: string, value: nil.type): BaradbQuery = + if not whereSymbols.contains(symbol): + raise newException(CatchableError, whereSymbolsError) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["or_where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "null" + }) + return self + + +proc whereBetween*(self: BaradbQuery, column: string, width: array[2, int|float]): BaradbQuery = + if self.query.hasKey("where_between") == false: + self.query["where_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereBetween*(self: BaradbQuery, column: string, width: array[2, string]): BaradbQuery = + if self.query.hasKey("where_between_string") == false: + self.query["where_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self: BaradbQuery, column: string, width: array[2, int|float]): BaradbQuery = + if self.query.hasKey("where_not_between") == false: + self.query["where_not_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self: BaradbQuery, column: string, width: array[2, string]): BaradbQuery = + if self.query.hasKey("where_not_between_string") == false: + self.query["where_not_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereIn*(self: BaradbQuery, column: string, width: seq[int|float|string]): BaradbQuery = + if self.query.hasKey("where_in") == false: + self.query["where_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotIn*(self: BaradbQuery, column: string, width: seq[int|float|string]): BaradbQuery = + if self.query.hasKey("where_not_in") == false: + self.query["where_not_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNull*(self: BaradbQuery, column: string): BaradbQuery = + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column + }] + else: + self.query["where_null"].add(%*{ + "column": column + }) + return self + + +proc groupBy*(self: BaradbQuery, column: string): BaradbQuery = + if self.query.hasKey("group_by") == false: + self.query["group_by"] = %*[{"column": column}] + else: + self.query["group_by"].add(%*{"column": column}) + return self + + +proc having*(self: BaradbQuery, column: string, symbol: string, + value: string|int|float|bool): BaradbQuery = + if not whereSymbols.contains(symbol): + raise newException(CatchableError, whereSymbolsError) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["having"].add(%*{ + "column": column, + "symbol": symbol, + "value": "?" + }) + return self + + +proc having*(self: BaradbQuery, column: string, symbol: string, value: nil.type): BaradbQuery = + if not whereSymbols.contains(symbol): + raise newException(CatchableError, whereSymbolsError) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["having"].add(%*{ + "column": column, + "symbol": symbol, + "value": "null" + }) + return self + + +proc orderBy*(self: BaradbQuery, column: string, order: Order): BaradbQuery = + if self.query.hasKey("order_by") == false: + self.query["order_by"] = %*[{ + "column": column, + "order": $order + }] + else: + self.query["order_by"].add(%*{ + "column": column, + "order": $order + }) + return self + + +proc limit*(self: BaradbQuery, num: int): BaradbQuery = + self.query["limit"] = %num + return self + + +proc offset*(self: BaradbQuery, num: int): BaradbQuery = + self.query["offset"] = %num + return self + + +proc raw*(self: BaradbConnections, sql: string, arges = newJArray()): RawBaradbQuery = + let rawQueryRdb = RawBaradbQuery( + log: self.log, + pools: self.pools, + query: newJObject(), + queryString: sql, + placeHolder: arges, + isInTransaction: false, + transactionConn: 0 + ) + return rawQueryRdb diff --git a/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_transaction.nim b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_transaction.nim new file mode 100644 index 0000000..6bf4dde --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_transaction.nim @@ -0,0 +1,42 @@ +import std/macros +import std/strutils +import std/strformat +import ../../models/baradb/baradb_types + + +when NimMajor == 1: + macro rdbTransaction(rdb: BaradbConnections, callback: untyped): untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body +else: + macro rdbTransaction(rdb: BaradbConnections, callback: untyped): untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except DbError, CatchableError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body + + +template transaction*(rdb: BaradbConnections, callback: untyped) = + rdbTransaction(rdb, callback) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_types.nim b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_types.nim new file mode 100644 index 0000000..15f27a9 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/baradb/baradb_types.nim @@ -0,0 +1,90 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/tables +import ../../log +import ../../libs/baradb/baradb_client + + +type Baradb* = object + + +const DEFAULT_CONN_MAX_LIFETIME_SECONDS* = 300 +const DEFAULT_CONN_MAX_IDLE_SECONDS* = 300 + + +type Connection* = ref object + client*: BaraClient + isBusy*: bool + createdAt*: int64 + lastUsedAt*: int64 + + +type BaradbPreparedEntry* = ref object + sql*: string + nArgs*: int + refCount*: int + lastUsedAt*: int64 + + +type Connections* = ref object + conns*: seq[Connection] + timeout*: int + maxConnectionLifetime*: int + maxConnectionIdleTime*: int + database*: string + user*: string + password*: string + host*: string + port*: int + waiters*: Deque[Future[void]] + columnTypeCache*: Table[string, seq[seq[string]]] + preparedCache*: Table[string, BaradbPreparedEntry] + + +type BaradbConnections* = ref object + log*: LogSetting + pools*: Connections + isInTransaction*: bool + transactionConn*: int + + +type BaradbPreparedContext* = ref object + owner*: BaradbConnections + connI*: int + + +type BaradbPreparedStatement* = ref object + owner*: BaradbConnections + entry*: BaradbPreparedEntry + sql*: string + nArgs*: int + isClosed*: bool + + +type BaradbQuery* = ref object + log*: LogSetting + pools*: Connections + query*: JsonNode + queryString*: string + placeHolder*: JsonNode + isInTransaction*: bool + transactionConn*: int + + +type RawBaradbQuery* = ref object + log*: LogSetting + pools*: Connections + query*: JsonNode + queryString*: string + placeHolder*: JsonNode + isInTransaction*: bool + transactionConn*: int + + +proc `$`*(self: BaradbConnections | BaradbQuery | RawBaradbQuery): string = + return "Baradb" + + +proc isConnected*(self: BaradbConnections | BaradbQuery | RawBaradbQuery): bool = + return self.pools.conns.len > 0 diff --git a/clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_builder.nim b/clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_builder.nim new file mode 100644 index 0000000..6943c2e --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_builder.nim @@ -0,0 +1,237 @@ +import std/json +import ../baradb_types +import ./baradb_generator + + +# ==================== SELECT ==================== + +proc selectBuilder*(self: BaradbQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + + +proc selectFirstBuilder*(self: BaradbQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .selectFirstSql() + .offsetSql() + .queryString + + +proc selectFindBuilder*(self: BaradbQuery, key: string): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .selectByIdSql(key) + .queryString + + +# ==================== INSERT ==================== + +proc insertValueBuilder*(self: BaradbQuery, items: JsonNode): string = + return self + .insertSql() + .insertValueSql(items) + .queryString + + +proc insertValuesBuilder*(self: BaradbQuery, rows: openArray[JsonNode]): string = + return self + .insertSql() + .insertValuesSql(rows) + .queryString + + +# ==================== UPDATE ==================== + +proc updateBuilder*(self: BaradbQuery, items: JsonNode): string = + return self + .updateSql() + .updateValuesSql(items) + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + + +# ==================== DELETE ==================== + +proc deleteBuilder*(self: BaradbQuery): string = + return self + .deleteSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + + +proc deleteByIdBuilder*(self: BaradbQuery, id: int, key: string): string = + return self + .deleteSql() + .fromSql() + .deleteByIdSql(id, key) + .queryString + + +# ==================== Aggregates ==================== + +proc countBuilder*(self: BaradbQuery): string = + return self + .selectCountSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + + +proc maxBuilder*(self: BaradbQuery, column: string): string = + return self + .selectMaxSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + + +proc minBuilder*(self: BaradbQuery, column: string): string = + return self + .selectMinSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + + +proc avgBuilder*(self: BaradbQuery, column: string): string = + return self + .selectAvgSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + + +proc sumBuilder*(self: BaradbQuery, column: string): string = + return self + .selectSumSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + + +proc columnBuilder*(self: BaradbQuery): string = + return self + .selectSql() + .fromSql() + .selectFirstSql() + .queryString diff --git a/clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_generator.nim b/clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_generator.nim new file mode 100644 index 0000000..b3f4a9b --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/baradb/query/baradb_generator.nim @@ -0,0 +1,397 @@ +import std/json +import std/strformat +import std/strutils +import ../baradb_types + + +proc quote(input:string):string = + var tmp = newSeq[string]() + for segment in input.split("."): + if segment.contains(" as "): + let parts = segment.split(" as ", maxsplit = 1) + let expression = parts[0] + let alias = parts[1] + if expression.contains("("): + let funcStart = expression.find('(') + let funcEnd = expression.find(')', funcStart) + let funcName = expression[0 ..< funcStart] + let columnName = expression[funcStart + 1 ..< funcEnd] + tmp.add(&"{funcName}(`{columnName}`) as `{alias}`") + else: + tmp.add(&"`{expression}` as `{alias}`") + elif segment.contains("("): + tmp.add(segment) + else: + tmp.add(&"`{segment}`") + return tmp.join(".") + + +# ==================== SELECT ==================== + +proc selectSql*(self: BaradbQuery): BaradbQuery = + var queryString = "" + + if self.query.hasKey("distinct"): + queryString.add("SELECT DISTINCT") + else: + queryString.add("SELECT") + + if self.query.hasKey("select"): + for i, item in self.query["select"].getElems(): + if i > 0: queryString.add(",") + var column = item.getStr() + if column != "*": column = quote(column) + queryString.add(&" {column}") + else: + queryString.add(" *") + + self.queryString = queryString + return self + + +proc fromSql*(self: BaradbQuery): BaradbQuery = + let table = self.query["table"].getStr() + self.queryString.add(&" FROM `{table}`") + return self + + +proc selectFirstSql*(self: BaradbQuery): BaradbQuery = + self.queryString.add(" LIMIT 1") + return self + + +proc selectByIdSql*(self: BaradbQuery, key: string): BaradbQuery = + let key = key.quote() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {key} = ? LIMIT 1") + else: + self.queryString.add(&" WHERE {key} = ? LIMIT 1") + return self + + +proc joinSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("join"): + for row in self.query["join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" INNER JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc leftJoinSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("left_join"): + for row in self.query["left_join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" LEFT JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc whereSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where"): + for i, row in self.query["where"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + if i == 0: + self.queryString.add(&" WHERE {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + return self + + +proc orWhereSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("or_where"): + for row in self.query["or_where"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" OR {column} {symbol} ?") + else: + self.queryString.add(&" WHERE {column} {symbol} ?") + return self + + +proc whereBetweenSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where_between"): + for row in self.query["where_between"]: + let column = row["column"].getStr().quote() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} BETWEEN ? AND ?") + else: + self.queryString.add(&" WHERE {column} BETWEEN ? AND ?") + return self + + +proc whereBetweenStringSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where_between_string"): + for row in self.query["where_between_string"]: + let column = row["column"].getStr().quote() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} BETWEEN ? AND ?") + else: + self.queryString.add(&" WHERE {column} BETWEEN ? AND ?") + return self + + +proc whereNotBetweenSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where_not_between"): + for row in self.query["where_not_between"]: + let column = row["column"].getStr().quote() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereNotBetweenStringSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where_not_between_string"): + for row in self.query["where_not_between_string"]: + let column = row["column"].getStr().quote() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereInSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where_in"): + var widthString = "" + for row in self.query["where_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} IN ({widthString})") + return self + + +proc whereNotInSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where_not_in"): + var widthString = "" + for row in self.query["where_not_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} NOT IN ({widthString})") + return self + + +proc whereNullSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("where_null"): + for row in self.query["where_null"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} {symbol} null") + else: + self.queryString.add(&" WHERE {column} {symbol} null") + return self + + +proc groupBySql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("group_by"): + for row in self.query["group_by"]: + let column = row["column"].getStr().quote() + if self.queryString.contains("GROUP BY"): + self.queryString.add(&", {column}") + else: + self.queryString.add(&" GROUP BY {column}") + return self + + +proc havingSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("having"): + for i, row in self.query["having"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + + if i == 0: + self.queryString.add(&" HAVING {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + + return self + + +proc orderBySql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("order_by"): + for row in self.query["order_by"]: + let column = row["column"].getStr().quote() + let order = row["order"].getStr() + + if self.queryString.contains("ORDER BY"): + self.queryString.add(&", {column} {order}") + else: + self.queryString.add(&" ORDER BY {column} {order}") + return self + + +proc limitSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("limit"): + let num = self.query["limit"].getInt() + self.queryString.add(&" LIMIT {num}") + + return self + + +proc offsetSql*(self: BaradbQuery): BaradbQuery = + if self.query.hasKey("offset"): + let num = self.query["offset"].getInt() + self.queryString.add(&" OFFSET {num}") + + return self + + +# ==================== INSERT ==================== + +proc insertSql*(self: BaradbQuery): BaradbQuery = + let table = self.query["table"].getStr() + self.queryString = &"INSERT INTO `{table}`" + return self + + +proc insertValueSql*(self: BaradbQuery, items: JsonNode): BaradbQuery = + var columns = "" + var values = "" + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: + columns.add(", ") + values.add(", ") + columns.add(&"`{key}`") + + self.placeHolder.add(%*{"key": key, "value": val}) + values.add("?") + + self.queryString.add(&" ({columns}) VALUES ({values})") + return self + + +proc insertValuesSql*(self: BaradbQuery, rows: openArray[JsonNode]): BaradbQuery = + var columns = "" + + var i = 0 + for key, value in rows[0]: + defer: i += 1 + if i > 0: columns.add(", ") + columns.add(&"`{key}`") + + var values = "" + var valuesCount = 0 + for items in rows: + var valueCount = 0 + var value = "" + for key, val in items.pairs: + defer: valueCount += 1 + if valueCount > 0: value.add(", ") + + self.placeHolder.add(%*{"key": key, "value": val}) + value.add("?") + + if valuesCount > 0: values.add(", ") + valuesCount += 1 + values.add(&"({value})") + + self.queryString.add(&" ({columns}) VALUES {values}") + return self + + +# ==================== UPDATE ==================== + +proc updateSql*(self: BaradbQuery): BaradbQuery = + var queryString = "" + queryString.add("UPDATE") + + var table = self.query["table"].getStr() + queryString.add(&" `{table}` SET") + self.queryString = queryString + return self + + +proc updateValuesSql*(self: BaradbQuery, items: JsonNode): BaradbQuery = + var value = "" + let placeHolder = newJArray() + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: value.add(",") + value.add(&" `{key}` = ?") + placeHolder.add(%*{"key": key, "value": val}) + + for row in self.placeHolder.items: + placeHolder.add(row) + + self.placeHolder = placeHolder + + self.queryString.add(value) + return self + + +# ==================== DELETE ==================== + +proc deleteSql*(self: BaradbQuery): BaradbQuery = + self.queryString = "DELETE" + return self + + +proc deleteByIdSql*(self: BaradbQuery, id: int, key: string): BaradbQuery = + self.queryString.add(&" WHERE `{key}` = ?") + return self + + +# ==================== Aggregates ==================== + +proc selectCountSql*(self: BaradbQuery): BaradbQuery = + let queryString = + if self.query.hasKey("select"): + let column = self.query["select"][0].getStr + &"`{column}`" + else: + "*" + self.queryString = &"SELECT count({queryString}) as aggregate" + return self + + +proc selectMaxSql*(self: BaradbQuery, column: string): BaradbQuery = + self.queryString = &"SELECT max(`{column}`) as aggregate" + return self + + +proc selectMinSql*(self: BaradbQuery, column: string): BaradbQuery = + self.queryString = &"SELECT min(`{column}`) as aggregate" + return self + + +proc selectAvgSql*(self: BaradbQuery, column: string): BaradbQuery = + self.queryString = &"SELECT avg(`{column}`) as aggregate" + return self + + +proc selectSumSql*(self: BaradbQuery, column: string): BaradbQuery = + self.queryString = &"SELECT sum(`{column}`) as aggregate" + return self diff --git a/clients/nim-allographer/src/allographer/query_builder/models/database_types.nim b/clients/nim-allographer/src/allographer/query_builder/models/database_types.nim new file mode 100644 index 0000000..5737d35 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/database_types.nim @@ -0,0 +1,71 @@ +type + Row* = seq[string] + + DbTypeKind* = enum ## a superset of datatypes that might be supported. + dbUnknown, ## unknown datatype + dbSerial, ## datatype used for primary auto-increment keys + dbNull, ## datatype used for the NULL value + dbBit, ## bit datatype + dbBool, ## boolean datatype + dbBlob, ## blob datatype + dbFixedChar, ## string of fixed length + dbVarchar, ## string datatype + dbJson, ## JSON datatype + dbXml, ## XML datatype + dbInt, ## some integer type + dbUInt, ## some unsigned integer type + dbDecimal, ## decimal numbers (fixed-point number) + dbFloat, ## some floating point type + dbDate, ## a year-month-day description + dbTime, ## HH:MM:SS information + dbDatetime, ## year-month-day and HH:MM:SS information, + ## plus optional time or timezone information + dbTimestamp, ## Timestamp values are stored as the number of seconds + ## since the epoch ('1970-01-01 00:00:00' UTC). + dbTimeInterval, ## an interval [a,b] of times + dbEnum, ## some enum + dbSet, ## set of enum values + dbArray, ## an array of values + dbComposite, ## composite type (record, struct, etc) + dbUrl, ## a URL + dbUuid, ## a UUID + dbInet, ## an IP address + dbMacAddress, ## a MAC address + dbGeometry, ## some geometric type + dbPoint, ## Point on a plane (x,y) + dbLine, ## Infinite line ((x1,y1),(x2,y2)) + dbLseg, ## Finite line segment ((x1,y1),(x2,y2)) + dbBox, ## Rectangular box ((x1,y1),(x2,y2)) + dbPath, ## Closed or open path (similar to polygon) ((x1,y1),...) + dbPolygon, ## Polygon (similar to closed path) ((x1,y1),...) + dbCircle, ## Circle <(x,y),r> (center point and radius) + dbUser1, ## user definable datatype 1 (for unknown extensions) + dbUser2, ## user definable datatype 2 (for unknown extensions) + dbUser3, ## user definable datatype 3 (for unknown extensions) + dbUser4, ## user definable datatype 4 (for unknown extensions) + dbUser5 ## user definable datatype 5 (for unknown extensions) + + DbType* = object ## describes a database type + kind*: DbTypeKind ## the kind of the described type + notNull*: bool ## does the type contain NULL? + name*: string ## the name of the type + size*: Natural ## the size of the datatype; 0 if of variable size + maxReprLen*: Natural ## maximal length required for the representation + precision*, scale*: Natural ## precision and scale of the number + min*, max*: BiggestInt ## the minimum and maximum of allowed values + validValues*: seq[string] ## valid values of an enum or a set + + DbColumn* = object ## information about a database column + name*: string ## name of the column + tableName*: string ## name of the table the column belongs to (optional) + typ*: DbType ## type of the column + primaryKey*: bool ## is this a primary key? + foreignKey*: bool ## is this a foreign key? + + DbColumns* = seq[DbColumn] + DbRows* = seq[DbColumns] + DbEffect* = object of IOEffect ## effect that denotes a database operation + ReadDbEffect* = object of DbEffect ## effect that denotes a read operation + WriteDbEffect* = object of DbEffect ## effect that denotes a write operation + +const errorConnectionNum* = 99999 diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_exec.nim b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_exec.nim new file mode 100644 index 0000000..62f8428 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_exec.nim @@ -0,0 +1,1229 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/options +import std/strformat +import std/strutils +import std/sequtils +import std/tables +import std/times +import ../../error +import ../../libs/mariadb/mariadb_impl +import ../../libs/mariadb/mariadb_rdb except Option, cuint +import ../../log +import ../database_types +import ../../prepared_param +import ./query/mariadb_builder +import ./mariadb_types + + +# ================================================================================ +# connection +# ================================================================================ + +proc getFreeConn(self:MariadbConnections | MariadbQuery | RawMariadbQuery):Future[int] {.async.} = + let deadline = getTime().toUnix() + self.pools.timeout + while true: + for i in 0..= deadline: + return errorConnectionNum + let w = newFuture[void]("getFreeConn.poolWait") + self.pools.waiters.addLast(w) + let remainingSec = deadline - now + var ms = int(remainingSec * 1000) + if ms < 1: + ms = 1 + let ok = await withTimeout(w, ms) + if not ok: + return errorConnectionNum + + +proc wakeOnePoolWaiter(pools: Connections) = + while pools.waiters.len > 0: + let w = pools.waiters.popFirst() + if w.finished: + continue + w.complete() + break + + +proc returnConn(self:MariadbConnections | MariadbQuery | RawMariadbQuery, i: int) {.async.} = + if i != errorConnectionNum: + self.pools.conns[i].isBusy = false + self.pools.conns[i].lastUsedAt = getTime().toUnix() + wakeOnePoolWaiter(self.pools) + + +proc raisePoolTimeout(self: MariadbConnections | MariadbQuery | RawMariadbQuery | MariadbPreparedStatement) {.noreturn.} = + raise newException(DbError, "Timed out while waiting for a free MariaDB connection") + + +proc touchStmtEntry(entry: MariadbPreparedEntry) = + entry.lastUsedAt = getTime().toUnix() + + +proc mustBeOpen(self: MariadbPreparedStatement) = + if self.isNil or self.isClosed: + raise newException(DbError, "MariaDB prepared statement is already closed") + + +proc hasPreparedEntry(cache: Table[string, MariadbPreparedEntry], sql: string): bool = + for key in cache.keys: + if key == sql: + return true + return false + + +proc getStmtEntry(self: MariadbConnections, sql: string): MariadbPreparedEntry = + if hasPreparedEntry(self.pools.preparedCache, sql): + return self.pools.preparedCache[sql] + let entry = MariadbPreparedEntry( + sql: sql, + nArgs: countQuestionMarks(sql), + stmts: newSeq[PSTMT](self.pools.conns.len), + refCount: 0, + lastUsedAt: getTime().toUnix(), + ) + self.pools.preparedCache[sql] = entry + return entry + + +proc nowUnix(): int64 = + getTime().toUnix() + + +proc hasConnExpired(self: Connections, conn: Connection): bool = + let now = nowUnix() + if self.maxConnectionLifetime > 0 and now - conn.createdAt >= self.maxConnectionLifetime.int64: + return true + if self.maxConnectionIdleTime > 0 and now - conn.lastUsedAt >= self.maxConnectionIdleTime.int64: + return true + return false + + +proc openMariadbConn(self: Connections): PMySQL = + let conn = mariadb_rdb.init(nil) + if conn == nil: + mariadb_rdb.close(conn) + dbError("mariadb_rdb.init() failed") + if mariadb_rdb.options(conn, MYSQL_OPT_NONBLOCK, nil) != 0: + let errmsg = $mariadb_rdb.error(conn) + mariadb_rdb.close(conn) + dbError(errmsg) + if mariadb_rdb.real_connect( + conn, + self.info.host.cstring, + self.info.user.cstring, + self.info.password.cstring, + self.info.database.cstring, + self.info.port.int32, + nil, + 0 + ) == nil: + let errmsg = $mariadb_rdb.error(conn) + mariadb_rdb.close(conn) + dbError(errmsg) + return conn + + +proc clearPreparedSlot(self: Connections, connI: int) = + for entry in self.preparedCache.values: + if connI < 0 or connI >= entry.stmts.len: + continue + if not entry.stmts[connI].isNil: + mariadb_impl.closePreparedStmt(entry.stmts[connI]) + entry.stmts[connI] = nil + + +proc refreshConn(self: Connections, connI: int): bool = + if connI < 0 or connI >= self.conns.len: + return false + let conn = openMariadbConn(self) + let oldConn = self.conns[connI].conn + self.clearPreparedSlot(connI) + if not oldConn.isNil: + mariadb_rdb.close(oldConn) + self.conns[connI].conn = conn + self.conns[connI].createdAt = nowUnix() + self.conns[connI].lastUsedAt = self.conns[connI].createdAt + return true + + +proc prepare*(self: MariadbConnections, sql: string): MariadbPreparedStatement = + new(result) + result.owner = self + result.info = self.info + result.entry = self.getStmtEntry(sql) + result.sql = sql + result.nArgs = result.entry.nArgs + result.entry.refCount += 1 + touchStmtEntry(result.entry) + result.resultBindCache = newSeq[MariadbResultBindCache](self.pools.conns.len) + + +proc ensurePreparedStmt(self: MariadbPreparedStatement, connI: int): Future[PSTMT] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "MariaDB prepared statement received an invalid connection index") + if self.entry.stmts[connI].isNil: + self.entry.stmts[connI] = await mariadb_impl.prepareStmt( + self.owner.pools.conns[connI].conn, + self.sql, + self.owner.pools.timeout + ) + touchStmtEntry(self.entry) + return self.entry.stmts[connI] + + +# ================================================================================ +# toJson +# ================================================================================ + +proc toJson(results:openArray[seq[string]], dbRows:DbRows):seq[JsonNode] = + var response_table = newSeq[JsonNode](results.len) + for index, rows in results.pairs: + var response_row = newJObject() + for i, row in rows: + let key = dbRows[index][i].name + let typ = dbRows[index][i].typ.kind + # let kindName = dbRows[index][i].typ.name + let size = dbRows[index][i].typ.size + + if typ == dbNull: + response_row[key] = newJNull() + elif [dbInt, dbUInt].contains(typ) and size == 1: + if row == "0": + response_row[key] = newJBool(false) + elif row == "1": + response_row[key] = newJBool(true) + elif [dbInt, dbUInt].contains(typ): + response_row[key] = newJInt(row.parseInt) + elif [dbDecimal, dbFloat].contains(typ): + response_row[key] = newJFloat(row.parseFloat) + elif [dbJson].contains(typ): + response_row[key] = row.parseJson + else: + response_row[key] = newJString(row) + + response_table[index] = response_row + return response_table + + +# ================================================================================ +# private exec +# ================================================================================ + +proc getAllRows(self:MariadbQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mariadb_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:MariadbQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mariadb_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + return rows + + +proc getRow(self:MariadbQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mariadb_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some # seq[JsonNode] + + +proc getRowPlain(self:MariadbQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mariadb_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + return rows[0] + + +proc getCachedColumnTypes(self: MariadbQuery, connI: int): Future[seq[seq[string]]] {.async.} = + let database = self.info.database + let table = self.query["table"].getStr + let cacheKey = database & "." & table + if cacheKey in self.pools.columnTypeCache: + return self.pools.columnTypeCache[cacheKey] + let columns = mariadb_impl.getColumnTypes( + self.pools.conns[connI].conn, database, table, self.pools.timeout + ).await + self.pools.columnTypeCache[cacheKey] = columns + return columns + + +proc exec(self:MariadbQuery, queryString:string) {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let columns = self.getCachedColumnTypes(connI).await + mariadb_impl.exec(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + + +proc insertId(self:MariadbQuery, queryString:string, key:string):Future[string] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let columns = self.getCachedColumnTypes(connI).await + let (rows, _) = mariadb_impl.execGetValue(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + return rows[0][0] + + +proc getAllRows(self:RawMariadbQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mariadb_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:RawMariadbQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + ## args is JArray [true, 1, 1.1, "str"] + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mariadb_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + return rows + + +proc getRow(self:RawMariadbQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mariadb_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some # seq[JsonNode] + + +proc getRowPlain(self:RawMariadbQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mariadb_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + return rows[0] + + +proc exec(self:RawMariadbQuery, queryString:string) {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + mariadb_impl.exec( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + +proc getColumns(self:MariadbQuery, queryString:string):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + return mariadb_impl.getColumns(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + +proc transactionStart(self:MariadbConnections) {.async.} = + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + self.isInTransaction = true + self.transactionConn = connI + + mariadb_impl.exec(self.pools.conns[connI].conn, "BEGIN", newJArray(), newSeq[seq[string]](), self.pools.timeout).await + + +proc transactionEnd(self:MariadbConnections, query:string) {.async.} = + defer: + self.returnConn(self.transactionConn).await + self.transactionConn = 0 + self.isInTransaction = false + + mariadb_impl.exec(self.pools.conns[self.transactionConn].conn, query, newJArray(), newSeq[seq[string]](), self.pools.timeout).await + + +proc withConn*( + self: MariadbConnections, + body: proc (ctx: MariadbPreparedContext): Future[void] +) {.async.} = + if self.isInTransaction: + let ctx = MariadbPreparedContext(owner: self, connI: self.transactionConn) + await body(ctx) + return + + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + defer: + self.returnConn(connI).await + + let ctx = MariadbPreparedContext(owner: self, connI: connI) + await body(ctx) + + +proc verifyCtx(self: MariadbPreparedStatement, ctx: MariadbPreparedContext) = + self.mustBeOpen() + if ctx.isNil: + raise newException(DbError, "MariaDB prepared context is nil") + if ctx.owner != self.owner: + raise newException(DbError, "MariaDB prepared context owner mismatch") + if ctx.connI < 0 or ctx.connI >= self.owner.pools.conns.len: + raise newException(DbError, "MariaDB prepared context has invalid connection index") + + +proc getPreparedRowsOnConn( + self: MariadbPreparedStatement, + connI: int, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "MariaDB prepared statement received an invalid connection index") + + let stmt = await self.ensurePreparedStmt(connI) + if connI >= self.resultBindCache.len: + self.resultBindCache.setLen(connI + 1) + if self.resultBindCache[connI].isNil: + new(self.resultBindCache[connI]) + return mariadb_impl.queryPreparedStmt( + self.owner.pools.conns[connI].conn, + stmt, + args, + self.owner.pools.timeout, + self.resultBindCache[connI] + ).await + + +proc getPreparedRows( + self: MariadbPreparedStatement, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + return await self.getPreparedRowsOnConn(connI, args) + + +proc getPreparedRows( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.verifyCtx(ctx) + return await self.getPreparedRowsOnConn(ctx.connI, args) + + +proc getPreparedAllRows(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getPreparedRow(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getPreparedAllRowsPlain(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getPreparedRows(args) + return rows + + +proc getPreparedRowPlain(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + let (rows, _) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc getPreparedAllRows( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getPreparedRow( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getPreparedAllRowsPlain( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getPreparedRows(ctx, args) + return rows + + +proc getPreparedRowPlain( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + let (rows, _) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc execPreparedOnConn( + self: MariadbPreparedStatement, + connI: int, + args: seq[PreparedParam] +) {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "MariaDB prepared statement received an invalid connection index") + + let stmt = await self.ensurePreparedStmt(connI) + await mariadb_impl.execPreparedStmt( + self.owner.pools.conns[connI].conn, + stmt, + args, + self.owner.pools.timeout + ) + + +proc execPrepared(self: MariadbPreparedStatement, args: seq[PreparedParam]) {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + await self.execPreparedOnConn(connI, args) + + +proc execPrepared( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +) {.async.} = + self.verifyCtx(ctx) + await self.execPreparedOnConn(ctx.connI, args) + + +proc preparedGet(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRows(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirst(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGetPlain(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRowsPlain(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirstPlain(self: MariadbPreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRowPlain(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedExec(self: MariadbPreparedStatement, args: seq[PreparedParam]) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGet( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRows(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirst( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGetPlain( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRowsPlain(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirstPlain( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRowPlain(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedExec( + self: MariadbPreparedStatement, + ctx: MariadbPreparedContext, + args: seq[PreparedParam] +) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc get*(self: MariadbPreparedStatement, args: seq[string]): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(args.toPreparedParams) + + +proc get*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: seq[string]): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(ctx, args.toPreparedParams) + + +proc get*(self: MariadbPreparedStatement, args: JsonNode): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(args.toPreparedParams) + + +proc get*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: JsonNode): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(ctx, args.toPreparedParams) + + +proc first*(self: MariadbPreparedStatement, args: seq[string]): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(args.toPreparedParams) + + +proc first*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: seq[string]): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(ctx, args.toPreparedParams) + + +proc first*(self: MariadbPreparedStatement, args: JsonNode): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(args.toPreparedParams) + + +proc first*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: JsonNode): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(ctx, args.toPreparedParams) + + +proc getPlain*(self: MariadbPreparedStatement, args: seq[string]): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(args.toPreparedParams) + + +proc getPlain*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: seq[string]): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(ctx, args.toPreparedParams) + + +proc getPlain*(self: MariadbPreparedStatement, args: JsonNode): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(args.toPreparedParams) + + +proc getPlain*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: JsonNode): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: MariadbPreparedStatement, args: seq[string]): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(args.toPreparedParams) + + +proc firstPlain*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: seq[string]): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: MariadbPreparedStatement, args: JsonNode): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(args.toPreparedParams) + + +proc firstPlain*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: JsonNode): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(ctx, args.toPreparedParams) + + +proc exec*(self: MariadbPreparedStatement, args: seq[string]) {.async.} = + await self.preparedExec(args.toPreparedParams) + + +proc exec*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: seq[string]) {.async.} = + await self.preparedExec(ctx, args.toPreparedParams) + + +proc exec*(self: MariadbPreparedStatement, args: JsonNode) {.async.} = + await self.preparedExec(args.toPreparedParams) + + +proc exec*(self: MariadbPreparedStatement, ctx: MariadbPreparedContext, args: JsonNode) {.async.} = + await self.preparedExec(ctx, args.toPreparedParams) + + +# ================================================================================ +# public exec +# ================================================================================ + +proc get*(self: MariadbQuery):Future[seq[JsonNode]] {.async.} = + let sql = self.selectBuilder() + try: + self.log.logger(sql) + return self.getAllRows(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc first*(self: MariadbQuery):Future[Option[JsonNode]] {.async.} = + var sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self: MariadbQuery, id:string, key="id"):Future[Option[JsonNode]] {.async.} = + self.placeHolder.add(%*{"key":key, "value": id}) + var sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self: MariadbQuery, id:int, key="id"):Future[Option[JsonNode]] {.async.} = + return self.find($id, key).await + + +proc getPlain*(self:MariadbQuery):Future[seq[seq[string]]] {.async.} = + var sql = self.selectBuilder() + try: + self.log.logger(sql) + return self.getAllRowsPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc firstPlain*(self:MariadbQuery):Future[seq[string]] {.async.} = + var sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:MariadbQuery, id: string, key="id"):Future[seq[string]] {.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + var sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:MariadbQuery, id: int, key="id"):Future[seq[string]] {.async.} = + return self.findPlain($id, key).await + + +# ==================== insert JsonNode ==================== +proc insert*(self:MariadbQuery, items:JsonNode) {.async.} = + ## items is `JObject` + var sql = self.insertValueBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*(self:MariadbQuery, items:seq[JsonNode]) {.async.} = + var sql = self.insertValuesBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*(self:MariadbQuery, items:JsonNode, key="id"):Future[string] {.async.} = + var sql = self.insertValueBuilder(items) + sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*(self: MariadbQuery, items: seq[JsonNode], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + var sql = self.insertValueBuilder(item) + sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +# ==================== insert Object ==================== +proc insert*[T](self:MariadbQuery, items:T) {.async.} = + var sql = self.insertValueBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*[T](self:MariadbQuery, items:seq[T]) {.async.} = + let items = items.mapIt(%it) + var sql = self.insertValuesBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*[T](self:MariadbQuery, items:T, key="id"):Future[string] {.async.} = + var sql = self.insertValueBuilder(%items) + sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*[T](self: MariadbQuery, items: seq[T], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + var sql = self.insertValueBuilder(%item) + sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +# ==================== update JsonNode ==================== +proc update*(self: MariadbQuery, items: JsonNode){.async.} = + var sql = self.updateBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc update*[T](self: MariadbQuery, items: T){.async.} = + var sql = self.updateBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self: MariadbQuery){.async.} = + var sql = self.deleteBuilder() + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self: MariadbQuery, id: int, key="id"){.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + var sql = self.deleteByIdBuilder(id, key) + self.log.logger(sql) + self.exec(sql).await + + +proc columns*(self:MariadbQuery):Future[seq[string]] {.async.} = + ## get columns sequence from table + var sql = self.columnBuilder() + try: + self.log.logger(sql) + return self.getColumns(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc count*(self:MariadbQuery):Future[int] {.async.} = + var sql = self.countBuilder() + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + return response.get["aggregate"].getInt() + else: + return 0 + + +proc min*(self:MariadbQuery, column:string):Future[Option[string]] {.async.} = + var sql = self.minBuilder(column) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc max*(self:MariadbQuery, column:string):Future[Option[string]] {.async.} = + var sql = self.maxBuilder(column) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc avg*(self:MariadbQuery, column:string):Future[Option[float]]{.async.} = + var sql = self.avgBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getFloat().some + else: + return none(float) + + +proc sum*(self:MariadbQuery, column:string):Future[Option[float]]{.async.} = + var sql = self.sumBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getFloat().some + else: + return none(float) + + +proc begin*(self:MariadbConnections) {.async.} = + self.log.logger("BEGIN") + self.transactionStart().await + + +proc rollback*(self:MariadbConnections) {.async.} = + self.log.logger("ROLLBACK") + self.transactionEnd("ROLLBACK").await + + +proc commit*(self:MariadbConnections) {.async.} = + self.log.logger("COMMIT") + self.transactionEnd("COMMIT").await + + +proc get*(self: RawMariadbQuery):Future[seq[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRows(self.queryString).await + + +proc getPlain*(self: RawMariadbQuery):Future[seq[seq[string]]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRowsPlain(self.queryString, self.placeHolder).await + + +proc exec*(self: RawMariadbQuery) {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + self.exec(self.queryString).await + + +proc first*(self: RawMariadbQuery):Future[Option[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRow(self.queryString).await + + +proc firstPlain*(self: RawMariadbQuery):Future[seq[string]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRowPlain(self.queryString, self.placeHolder).await + + +proc close*(self: MariadbPreparedStatement) {.async.} = + if self.isNil or self.isClosed: + return + self.isClosed = true + if not self.entry.isNil: + if self.entry.refCount > 0: + self.entry.refCount -= 1 + touchStmtEntry(self.entry) + + +proc flushStmt*(self: MariadbConnections, sql: string) {.async.} = + if not hasPreparedEntry(self.pools.preparedCache, sql): + return + let entry = self.pools.preparedCache[sql] + for i, stmt in entry.stmts: + if stmt.isNil: + continue + try: + mariadb_impl.closePreparedStmt(stmt) + except CatchableError: + self.log.echoErrorMsg("close failed for prepared stmt: " & getCurrentExceptionMsg()) + entry.stmts[i] = nil + self.pools.preparedCache.del(sql) + + +proc clearStmtCache*(self: MariadbConnections) {.async.} = + let keys = toSeq(self.pools.preparedCache.keys) + for sql in keys: + await self.flushStmt(sql) + + +template seeder*(rdb:MariadbConnections, tableName:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table is empty. + block: + if rdb.table(tableName).count().waitFor == 0: + body + + +template seeder*(rdb:MariadbConnections, tableName, column:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table or specified column is empty. + block: + if rdb.table(tableName).select(column).count().waitFor == 0: + body diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_open.nim b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_open.nim new file mode 100644 index 0000000..20060a9 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_open.nim @@ -0,0 +1,90 @@ +import std/asyncdispatch +import std/deques +import std/tables +import std/times +import ../../libs/database_url +import ../../libs/mariadb/mariadb_rdb +import ../../error +import ../../log +import ./mariadb_types + + +proc dbOpen*(_: type MariaDB, database: string, user: string, password: string, + host: string, port: int, maxConnections=1, timeout=30, + shouldDisplayLog=false, shouldOutputLogFile=false, logDir="", + maxConnectionLifetime=DEFAULT_CONN_MAX_LIFETIME_SECONDS, + maxConnectionIdleTime=DEFAULT_CONN_MAX_IDLE_SECONDS): MariadbConnections = + var conns = newSeq[Connection](maxConnections) + for i in 0..=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"] +const whereSymbolsError = """Arg position 3 is only allowed of ["is", "is not", "=", "!=", "<", "<=", ">=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"]""" + +proc where*(self: MariadbQuery, column: string, symbol: string, + value: string|int|float): MariadbQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: MariadbQuery, column: string, symbol: string, + value: bool): MariadbQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: MariadbQuery, column: string, symbol: string, value: nil.type): MariadbQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["where_null"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orWhere*(self: MariadbQuery, column: string, symbol: string, + value: string|int|float|bool): MariadbQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["or_where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc orWhere*(self: MariadbQuery, column: string, symbol: string, value: nil.type): MariadbQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["or_where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "null" + }) + return self + + +proc whereBetween*(self:MariadbQuery, column:string, width:array[2, int|float]): MariadbQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_between") == false: + self.query["where_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereBetween*(self:MariadbQuery, column:string, width:array[2, string]): MariadbQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_between_string") == false: + self.query["where_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self:MariadbQuery, column:string, width:array[2, int|float]): MariadbQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_not_between") == false: + self.query["where_not_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self:MariadbQuery, column:string, width:array[2, string]): MariadbQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_not_between_string") == false: + self.query["where_not_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereIn*(self:MariadbQuery, column:string, width:seq[int|float|string]): MariadbQuery = + for row in width: + self.placeHolder.add(%*{"key": column, "value": row}) + + if self.query.hasKey("where_in") == false: + self.query["where_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotIn*(self:MariadbQuery, column:string, width:seq[int|float|string]): MariadbQuery = + for row in width: + self.placeHolder.add(%*{"key": column, "value": row}) + + if self.query.hasKey("where_not_in") == false: + self.query["where_not_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNull*(self:MariadbQuery, column:string): MariadbQuery = + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": "is", + }] + else: + self.query["where_null"].add(%*{ + "column": column, + "symbol": "is", + }) + return self + + +proc groupBy*(self:MariadbQuery, column:string): MariadbQuery = + if self.query.hasKey("group_by") == false: + self.query["group_by"] = %*[{"column": column}] + else: + self.query["group_by"].add(%*{"column": column}) + return self + + +proc having*(self: MariadbQuery, column: string, symbol: string, + value: string|int|float|bool): MariadbQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + +proc having*(self: MariadbQuery, column: string, symbol: string, value: nil.type): MariadbQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orderBy*(self:MariadbQuery, column:string, order:Order): MariadbQuery = + if self.query.hasKey("order_by") == false: + self.query["order_by"] = %*[{ + "column": column, + "order": $order + }] + else: + self.query["order_by"].add(%*{ + "column": column, + "order": $order + }) + return self + + +proc limit*(self: MariadbQuery, num: int): MariadbQuery = + self.query["limit"] = %num + return self + + +proc offset*(self: MariadbQuery, num: int): MariadbQuery = + self.query["offset"] = %num + return self + + +proc raw*(self:MariadbConnections, sql:string, arges=newJArray()): RawMariadbQuery = + ## arges is `JArray` `[true, 1, 1.1, "str"]` + ## + ## can't use BLOB data. + let rawQueryRdb = RawMariadbQuery( + log: self.log, + pools: self.pools, + info: self.info, + query: newJObject(), + queryString: sql.strip(), + placeHolder: arges, + isInTransaction: false, + transactionConn: 0 + ) + return rawQueryRdb diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_transaction.nim b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_transaction.nim new file mode 100644 index 0000000..1924bfd --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_transaction.nim @@ -0,0 +1,42 @@ +import std/macros +import std/strutils +import std/strformat +import ../../models/mariadb/mariadb_types + + +when NimMajor == 1: + macro rdbTransaction(rdb:MariadbConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except DbError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body +else: + macro rdbTransaction(rdb:MariadbConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except CatchableError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body + + +template transaction*(rdb:MariadbConnections, callback: untyped) = + rdbTransaction(rdb, callback) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_types.nim b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_types.nim new file mode 100644 index 0000000..bdc8d41 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/mariadb_types.nim @@ -0,0 +1,115 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/tables +import ../../log +import ../../libs/mariadb/mariadb_rdb + + +type MariaDB* = object + + +const DEFAULT_CONN_MAX_LIFETIME_SECONDS* = 300 +const DEFAULT_CONN_MAX_IDLE_SECONDS* = 300 + + +type ConnectionInfo* = object + database*:string + user*:string + password*:string + host*:string + port*:int + + +type Connection* = object + conn*: PMySQL + isBusy*: bool + createdAt*: int64 + lastUsedAt*: int64 + + +type MariadbPreparedEntry* = ref object + sql*: string + nArgs*: int + stmts*: seq[PSTMT] + refCount*: int + lastUsedAt*: int64 + + +type Connections* = ref object + conns*: seq[Connection] + timeout*:int + maxConnectionLifetime*: int + maxConnectionIdleTime*: int + info*: ConnectionInfo + waiters*: Deque[Future[void]] + columnTypeCache*: Table[string, seq[seq[string]]] + preparedCache*: Table[string, MariadbPreparedEntry] + + +## created by `let rdb = dbOpen(MySQL, "localhost", 3306)` +type MariadbConnections* = ref object + log*: LogSetting + pools*:Connections + info*:ConnectionInfo + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type MariadbPreparedContext* = ref object + owner*: MariadbConnections + connI*: int + + +## created by `rdb.select("columnName")` or `rdb.table("tableName")` +type MariadbQuery* = ref object + log*: LogSetting + pools*:Connections + info*:ConnectionInfo + query*: JsonNode + queryString*: string + placeHolder*: JsonNode # [{"key":"user", "value":"user1"}] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type RawMariadbQuery* = ref object + log*: LogSetting + pools*:Connections + info*:ConnectionInfo + query*: JsonNode + queryString*: string + placeHolder*: JsonNode # ["user1", "user1@example.com"] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type MariadbResultBindCache* = ref object + binds*: seq[BIND] + buffers*: seq[string] + lengths*: seq[culong] + nullFlags*: seq[my_bool] + errorFlags*: seq[my_bool] + + +type MariadbPreparedStatement* = ref object + owner*: MariadbConnections + info*: ConnectionInfo + entry*: MariadbPreparedEntry + sql*: string + nArgs*: int + isClosed*: bool + resultBindCache*: seq[MariadbResultBindCache] + + + + +proc `$`*(self:MariadbConnections|MariadbQuery|RawMariadbQuery):string = + return "MariaDB" + + +proc isConnected*(self:MariadbConnections|MariadbQuery|RawMariadbQuery):bool = + return self.pools.conns.len > 0 diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_builder.nim b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_builder.nim new file mode 100644 index 0000000..e55b679 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_builder.nim @@ -0,0 +1,228 @@ +import std/json +import ../mariadb_types +import ./mariadb_generator + + +# ==================== SELECT ==================== + +proc selectBuilder*(self: MariadbQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc selectFirstBuilder*(self: MariadbQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .selectFirstSql() + .offsetSql() + .queryString + +proc selectFindBuilder*(self: MariadbQuery, key: string): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .selectByIdSql(key) + .queryString + + +# ==================== INSERT ==================== + +proc insertValueBuilder*(self: MariadbQuery, items: JsonNode): string = + return self + .insertSql() + .insertValueSql(items) + .queryString + +proc insertValuesBuilder*(self: MariadbQuery, rows: openArray[JsonNode]): string = + return self + .insertSql() + .insertValuesSql(rows) + .queryString + + +# ==================== UPDATE ==================== + +proc updateBuilder*(self: MariadbQuery, items: JsonNode): string = + return self + .updateSql() + .updateValuesSql(items) + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + + +# ==================== DELETE ==================== + +proc deleteBuilder*(self: MariadbQuery): string = + return self + .deleteSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + +proc deleteByIdBuilder*(self: MariadbQuery, id: int, key: string): string = + return self + .deleteSql() + .fromSql() + .deleteByIdSql(id, key) + .queryString + + +# ==================== Aggregates ==================== + +proc countBuilder*(self:MariadbQuery): string = + return self + .selectCountSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc maxBuilder*(self:MariadbQuery, column:string): string = + return self + .selectMaxSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc minBuilder*(self:MariadbQuery, column:string): string = + return self + .selectMinSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc avgBuilder*(self:MariadbQuery, column:string): string = + return self + .selectAvgSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc sumBuilder*(self:MariadbQuery, column:string): string = + return self + .selectSumSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc columnBuilder*(self:MariadbQuery): string = + return self + .selectSql() + .fromSql() + .selectFirstSql() + .queryString diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_generator.nim b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_generator.nim new file mode 100644 index 0000000..1864ea1 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mariadb/query/mariadb_generator.nim @@ -0,0 +1,429 @@ +import std/json +import std/strformat +import std/strutils +# import ../../database_types +import ../mariadb_types + + +proc quote(input:string):string = + ## "User.id as userId" => ```User```.```id``` as ```userId``` + ## + ## "COUNT(id) as count" => ```COUNT(id)``` as ```count``` + ## + ## "MAX(id) as maxId" => ```MAX(id)``` as ```maxId``` + ## + var tmp = newSeq[string]() + for segment in input.split("."): + if segment.contains(" as "): + # Split once on ' as ' to separate the expression and its alias + let parts = segment.split(" as ", maxsplit = 1) + let expression = parts[0] + let alias = parts[1] + if expression.contains("("): + # Extract function name and column name using index-based slicing + let funcStart = expression.find('(') + let funcEnd = expression.find(')', funcStart) + let funcName = expression[0 ..< funcStart] + let columnName = expression[funcStart + 1 ..< funcEnd] + tmp.add(&"{funcName}(`{columnName}`) as `{alias}`") + else: + # Quote the expression and alias + tmp.add(&"`{expression}` as `{alias}`") + elif segment.contains("("): + # Leave functions without alias as is + tmp.add(segment) + else: + # Quote standalone identifiers + tmp.add(&"`{segment}`") + return tmp.join(".") + + +# ==================== SELECT ==================== + +proc selectSql*(self: MariadbQuery): MariadbQuery = + var queryString = "" + + if self.query.hasKey("distinct"): + queryString.add("SELECT DISTINCT") + else: + queryString.add("SELECT") + + if self.query.hasKey("select"): + for i, item in self.query["select"].getElems(): + if i > 0: queryString.add(",") + var column = item.getStr() + if column != "*": column = quote(column) + queryString.add(&" {column}") + else: + queryString.add(" *") + + self.queryString = queryString + return self + + +proc fromSql*(self: MariadbQuery): MariadbQuery = + let table = self.query["table"].getStr() + self.queryString.add(&" FROM `{table}`") + return self + + +proc selectFirstSql*(self: MariadbQuery): MariadbQuery = + self.queryString.add(" LIMIT 1") + return self + + +proc selectByIdSql*(self: MariadbQuery, key:string): MariadbQuery = + let key = key.quote() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {key} = ? LIMIT 1") + else: + self.queryString.add(&" WHERE {key} = ? LIMIT 1") + return self + + +proc joinSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("join"): + for row in self.query["join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" INNER JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc leftJoinSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("left_join"): + for row in self.query["left_join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" LEFT JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc whereSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where"): + for i, row in self.query["where"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + if i == 0: + self.queryString.add(&" WHERE {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + return self + + +proc orWhereSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("or_where"): + for row in self.query["or_where"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" OR {column} {symbol} ?") + else: + self.queryString.add(&" WHERE {column} {symbol} ?") + return self + + +proc whereBetweenSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where_between"): + for row in self.query["where_between"]: + let column = row["column"].getStr() + # let start = row["width"][0].getFloat() + # let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` BETWEEN {start} AND {stop}") + self.queryString.add(&" AND `{column}` BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` BETWEEN {start} AND {stop}") + self.queryString.add(&" WHERE `{column}` BETWEEN ? AND ?") + return self + + +proc whereBetweenStringSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where_between_string"): + for row in self.query["where_between_string"]: + let column = row["column"].getStr() + # let start = row["width"][0].getStr + # let stop = row["width"][1].getStr + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" AND `{column}` BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" WHERE `{column}` BETWEEN ? AND ?") + return self + + +proc whereNotBetweenSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where_not_between"): + for row in self.query["where_not_between"]: + let column = row["column"].getStr().quote() + # let start = row["width"][0].getFloat() + # let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` NOT BETWEEN {start} AND {stop}") + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` NOT BETWEEN {start} AND {stop}") + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereNotBetweenStringSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where_not_between_string"): + for row in self.query["where_not_between_string"]: + let column = row["column"].getStr().quote() + # let start = row["width"][0].getStr + # let stop = row["width"][1].getStr + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` NOT BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` NOT BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereInSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where_in"): + var widthString = "" + for row in self.query["where_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} IN ({widthString})") + return self + + +proc whereNotInSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where_not_in"): + var widthString = "" + for row in self.query["where_not_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} NOT IN ({widthString})") + return self + + +proc whereNullSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("where_null"): + for row in self.query["where_null"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} {symbol} NULL") + else: + self.queryString.add(&" WHERE {column} {symbol} NULL") + return self + + +proc groupBySql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("group_by"): + for row in self.query["group_by"]: + let column = row["column"].getStr().quote() + if self.queryString.contains("GROUP BY"): + self.queryString.add(&", {column}") + else: + self.queryString.add(&" GROUP BY {column}") + return self + + +proc havingSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("having"): + for i, row in self.query["having"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + + if i == 0: + self.queryString.add(&" HAVING {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + + return self + + +proc orderBySql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("order_by"): + for row in self.query["order_by"]: + let column = row["column"].getStr().quote() + let order = row["order"].getStr() + + if self.queryString.contains("ORDER BY"): + self.queryString.add(&", {column} {order}") + else: + self.queryString.add(&" ORDER BY {column} {order}") + return self + + +proc limitSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("limit"): + let num = self.query["limit"].getInt() + self.queryString.add(&" LIMIT {num}") + + return self + + +proc offsetSql*(self: MariadbQuery): MariadbQuery = + if self.query.hasKey("offset"): + let num = self.query["offset"].getInt() + self.queryString.add(&" OFFSET {num}") + + return self + + +# ==================== INSERT ==================== + +proc insertSql*(self: MariadbQuery): MariadbQuery = + let table = self.query["table"].getStr() + self.queryString = &"INSERT INTO `{table}`" + return self + + +proc insertValueSql*(self: MariadbQuery, items: JsonNode): MariadbQuery = + var columns = "" + var values = "" + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: + columns.add(", ") + values.add(", ") + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"`{key}`") + + self.placeHolder.add(%*{"key":key, "value":val}) + values.add("?") + + self.queryString.add(&" ({columns}) VALUES ({values})") + return self + + +proc insertValuesSql*(self: MariadbQuery, rows: openArray[JsonNode]): MariadbQuery = + var columns = "" + + var i = 0 + for key, value in rows[0]: + defer: i += 1 + if i > 0: columns.add(", ") + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"`{key}`") + + var values = "" + var valuesCount = 0 + for items in rows: + var valueCount = 0 + var value = "" + for key, val in items.pairs: + defer: valueCount += 1 + if valueCount > 0: value.add(", ") + + self.placeHolder.add(%*{"key":key, "value":val}) + value.add("?") + + if valuesCount > 0: values.add(", ") + valuesCount += 1 + values.add(&"({value})") + + self.queryString.add(&" ({columns}) VALUES {values}") + return self + + +# ==================== UPDATE ==================== + +proc updateSql*(self: MariadbQuery): MariadbQuery = + var queryString = "" + queryString.add("UPDATE") + + var table = self.query["table"].getStr() + queryString.add(&" `{table}` SET") + self.queryString = queryString + return self + + +proc updateValuesSql*(self: MariadbQuery, items:JsonNode): MariadbQuery = + var value = "" + let placeHolder = newJArray() + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: value.add(",") + value.add(&" `{key}` = ?") + placeHolder.add(%*{"key":key, "value":val}) + + for row in self.placeHolder.items: + placeHolder.add(row) + + self.placeHolder = placeHolder + + self.queryString.add(value) + return self + + +# ==================== DELETE ==================== + +proc deleteSql*(self: MariadbQuery): MariadbQuery = + self.queryString = "DELETE" + return self + + +proc deleteByIdSql*(self: MariadbQuery, id: int, key: string): MariadbQuery = + self.queryString.add(&" WHERE `{key}` = ?") + return self + +# ==================== Aggregates ==================== + +proc selectCountSql*(self: MariadbQuery): MariadbQuery = + let queryString = + if self.query.hasKey("select"): + let column = self.query["select"][0].getStr + &"`{column}`" + else: + "*" + self.queryString = &"SELECT count({queryString}) as aggregate" + return self + + +proc selectMaxSql*(self: MariadbQuery, column:string): MariadbQuery = + self.queryString = &"SELECT max(`{column}`) as aggregate" + return self + + +proc selectMinSql*(self: MariadbQuery, column:string): MariadbQuery = + self.queryString = &"SELECT min(`{column}`) as aggregate" + return self + + +proc selectAvgSql*(self: MariadbQuery, column:string): MariadbQuery = + self.queryString = &"SELECT avg(`{column}`) as aggregate" + return self + + +proc selectSumSql*(self: MariadbQuery, column:string): MariadbQuery = + self.queryString = &"SELECT sum(`{column}`) as aggregate" + return self diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_exec.nim b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_exec.nim new file mode 100644 index 0000000..a362300 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_exec.nim @@ -0,0 +1,1196 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import std/strutils +import std/sequtils +import std/tables +import std/times +import ../../error +import ../../libs/mysql/mysql_impl +import ../../libs/mysql/mysql_rdb except Option +import ../../log +import ../database_types +import ../../prepared_param +import ./query/mysql_builder +import ./mysql_types + + +# ================================================================================ +# connection +# ================================================================================ + +proc getFreeConn(self:MysqlConnections | MysqlQuery | RawMysqlQuery):Future[int] {.async.} = + let calledAt = getTime().toUnix() + while true: + for i in 0..= calledAt + self.pools.timeout: + return errorConnectionNum + + +proc returnConn(self:MysqlConnections | MysqlQuery | RawMysqlQuery, i: int) {.async.} = + if i != errorConnectionNum: + self.pools.conns[i].isBusy = false + self.pools.conns[i].lastUsedAt = getTime().toUnix() + + +proc raisePoolTimeout(self: MysqlConnections | MysqlQuery | RawMysqlQuery | MysqlPreparedStatement) {.noreturn.} = + raise newException(DbError, "Timed out while waiting for a free MySQL connection") + + +proc touchStmtEntry(entry: MysqlPreparedEntry) = + entry.lastUsedAt = getTime().toUnix() + + +proc mustBeOpen(self: MysqlPreparedStatement) = + if self.isNil or self.isClosed: + raise newException(DbError, "MySQL prepared statement is already closed") + + +proc hasPreparedEntry(cache: Table[string, MysqlPreparedEntry], sql: string): bool = + for key in cache.keys: + if key == sql: + return true + return false + + +proc getStmtEntry(self: MysqlConnections, sql: string): MysqlPreparedEntry = + if hasPreparedEntry(self.pools.preparedCache, sql): + return self.pools.preparedCache[sql] + let entry = MysqlPreparedEntry( + sql: sql, + nArgs: countQuestionMarks(sql), + stmts: newSeq[PSTMT](self.pools.conns.len), + refCount: 0, + lastUsedAt: getTime().toUnix(), + ) + self.pools.preparedCache[sql] = entry + return entry + + +proc nowUnix(): int64 = + getTime().toUnix() + + +proc hasConnExpired(self: Connections, conn: Connection): bool = + let now = nowUnix() + if self.maxConnectionLifetime > 0 and now - conn.createdAt >= self.maxConnectionLifetime.int64: + return true + if self.maxConnectionIdleTime > 0 and now - conn.lastUsedAt >= self.maxConnectionIdleTime.int64: + return true + return false + + +proc openMysqlConn(self: Connections): PMySQL = + let conn = mysql_rdb.init(nil) + if conn == nil: + mysql_rdb.close(conn) + dbError("mysql_rdb.init() failed") + if mysql_rdb.real_connect( + conn, + self.info.host.cstring, + self.info.user.cstring, + self.info.password.cstring, + self.info.database.cstring, + self.info.port.int32, + nil, + 0 + ) == nil: + let errmsg = $mysql_rdb.error(conn) + mysql_rdb.close(conn) + dbError(errmsg) + return conn + + +proc clearPreparedSlot(self: Connections, connI: int) = + for entry in self.preparedCache.values: + if connI < 0 or connI >= entry.stmts.len: + continue + if not entry.stmts[connI].isNil: + mysql_impl.closePreparedStmt(entry.stmts[connI]) + entry.stmts[connI] = nil + + +proc refreshConn(self: Connections, connI: int): bool = + if connI < 0 or connI >= self.conns.len: + return false + let conn = openMysqlConn(self) + let oldConn = self.conns[connI].conn + self.clearPreparedSlot(connI) + if not oldConn.isNil: + mysql_rdb.close(oldConn) + self.conns[connI].conn = conn + self.conns[connI].createdAt = nowUnix() + self.conns[connI].lastUsedAt = self.conns[connI].createdAt + return true + + +# ================================================================================ +# toJson +# ================================================================================ + +proc toJson(results:openArray[seq[string]], dbRows:DbRows):seq[JsonNode] = + var response_table = newSeq[JsonNode](results.len) + for index, rows in results.pairs: + var response_row = newJObject() + for i, row in rows: + let key = dbRows[index][i].name + let typ = dbRows[index][i].typ.kind + # let kindName = dbRows[index][i].typ.name + let size = dbRows[index][i].typ.size + + if typ == dbNull: + response_row[key] = newJNull() + elif [dbInt, dbUInt].contains(typ) and size == 1: + if row == "0": + response_row[key] = newJBool(false) + elif row == "1": + response_row[key] = newJBool(true) + elif [dbInt, dbUInt].contains(typ): + response_row[key] = newJInt(row.parseInt) + elif [dbDecimal, dbFloat].contains(typ): + response_row[key] = newJFloat(row.parseFloat) + elif [dbJson].contains(typ): + response_row[key] = row.parseJson + else: + response_row[key] = newJString(row) + + response_table[index] = response_row + return response_table + + +# ================================================================================ +# private exec +# ================================================================================ + +proc getAllRows(self:MysqlQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mysql_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:MysqlQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mysql_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + return rows + + +proc getRow(self:MysqlQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mysql_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some # seq[JsonNode] + + +proc getRowPlain(self:MysqlQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mysql_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + return rows[0] + + +proc exec(self:MysqlQuery, queryString:string) {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let database = self.info.database + let table = self.query["table"].getStr + let columns = mysql_impl.getColumnTypes(self.pools.conns[connI].conn, $database, table, self.pools.timeout).await + mysql_impl.exec(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + + +proc insertId(self:MysqlQuery, queryString:string, key:string):Future[string] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let table = self.query["table"].getStr + let columnGetQuery = &"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '{table}'" + let (columns, _) = mysql_impl.query(self.pools.conns[connI].conn, columnGetQuery, newJArray(), self.pools.timeout).await + + # let (rows, _) = mysql_impl.execGetValue(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + # return rows[0][0] + mysql_impl.exec(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + let (rows, _) = mysql_impl.query(self.pools.conns[connI].conn, "SELECT LAST_INSERT_ID()", self.placeHolder, self.pools.timeout).await + return rows[0][0] + + +proc getAllRows(self:RawMysqlQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mysql_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:RawMysqlQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + ## args is JArray [true, 1, 1.1, "str"] + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mysql_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + return rows + + +proc getRow(self:RawMysqlQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = mysql_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some # seq[JsonNode] + + +proc getRowPlain(self:RawMysqlQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = mysql_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + return rows[0] + + +proc exec(self:RawMysqlQuery, queryString:string) {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + mysql_impl.exec( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + +proc getColumns(self:MysqlQuery, queryString:string):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + return mysql_impl.getColumns(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + +proc transactionStart(self:MysqlConnections) {.async.} = + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + self.isInTransaction = true + self.transactionConn = connI + + mysql_impl.exec(self.pools.conns[connI].conn, "BEGIN", newJArray(), newSeq[seq[string]](), self.pools.timeout).await + + +proc transactionEnd(self:MysqlConnections, query:string) {.async.} = + defer: + self.returnConn(self.transactionConn).await + self.transactionConn = 0 + self.isInTransaction = false + + mysql_impl.exec(self.pools.conns[self.transactionConn].conn, query, newJArray(), newSeq[seq[string]](), self.pools.timeout).await + + +# ================================================================================ +# public exec +# ================================================================================ + +proc get*(self: MysqlQuery):Future[seq[JsonNode]] {.async.} = + let sql = self.selectBuilder() + try: + self.log.logger(sql) + return self.getAllRows(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc first*(self: MysqlQuery):Future[Option[JsonNode]] {.async.} = + var sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self: MysqlQuery, id:string, key="id"):Future[Option[JsonNode]] {.async.} = + self.placeHolder.add(%*{"key":key, "value": id}) + var sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self: MysqlQuery, id:int, key="id"):Future[Option[JsonNode]] {.async.} = + return self.find($id, key).await + + +proc getPlain*(self:MysqlQuery):Future[seq[seq[string]]] {.async.} = + var sql = self.selectBuilder() + try: + self.log.logger(sql) + return self.getAllRowsPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc firstPlain*(self:MysqlQuery):Future[seq[string]] {.async.} = + var sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:MysqlQuery, id: string, key="id"):Future[seq[string]] {.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + var sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:MysqlQuery, id: int, key="id"):Future[seq[string]] {.async.} = + return self.findPlain($id, key).await + + +# ==================== intert JsonNode ==================== +proc insert*(self:MysqlQuery, items:JsonNode) {.async.} = + ## items is `JObject` + var sql = self.insertValueBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*(self:MysqlQuery, items:seq[JsonNode]) {.async.} = + var sql = self.insertValuesBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*(self:MysqlQuery, items:JsonNode, key="id"):Future[string] {.async.} = + var sql = self.insertValueBuilder(items) + # sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*(self: MysqlQuery, items: seq[JsonNode], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + var sql = self.insertValueBuilder(item) + # sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +# ==================== intert Object ==================== +proc insert*[T](self:MysqlQuery, items:T) {.async.} = + var sql = self.insertValueBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*[T](self:MysqlQuery, items:seq[T]) {.async.} = + let items = items.mapIt(%it) + var sql = self.insertValuesBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*[T](self:MysqlQuery, items:T, key="id"):Future[string] {.async.} = + var sql = self.insertValueBuilder(%items) + # sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*[T](self: MysqlQuery, items: seq[T], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + var sql = self.insertValueBuilder(%item) + # sql.add(&" RETURNING `{key}`") + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +proc update*(self: MysqlQuery, items: JsonNode){.async.} = + var sql = self.updateBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc update*[T](self: MysqlQuery, items: T){.async.} = + var sql = self.updateBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self: MysqlQuery){.async.} = + var sql = self.deleteBuilder() + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self: MysqlQuery, id: int, key="id"){.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + var sql = self.deleteByIdBuilder(id, key) + self.log.logger(sql) + self.exec(sql).await + + +proc columns*(self:MysqlQuery):Future[seq[string]] {.async.} = + ## get columns sequence from table + var sql = self.columnBuilder() + try: + self.log.logger(sql) + return self.getColumns(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc count*(self:MysqlQuery):Future[int] {.async.} = + var sql = self.countBuilder() + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + return response.get["aggregate"].getInt() + else: + return 0 + + +proc min*(self:MysqlQuery, column:string):Future[Option[string]] {.async.} = + var sql = self.minBuilder(column) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc max*(self:MysqlQuery, column:string):Future[Option[string]] {.async.} = + var sql = self.maxBuilder(column) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc avg*(self:MysqlQuery, column:string):Future[Option[float]]{.async.} = + var sql = self.avgBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getFloat().some + else: + return none(float) + + +proc sum*(self:MysqlQuery, column:string):Future[Option[float]]{.async.} = + var sql = self.sumBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getFloat().some + else: + return none(float) + + +proc begin*(self:MysqlConnections) {.async.} = + self.log.logger("BEGIN") + self.transactionStart().await + + +proc rollback*(self:MysqlConnections) {.async.} = + self.log.logger("ROLLBACK") + self.transactionEnd("ROLLBACK").await + + +proc commit*(self:MysqlConnections) {.async.} = + self.log.logger("COMMIT") + self.transactionEnd("COMMIT").await + + +proc withConn*( + self: MysqlConnections, + body: proc (ctx: MysqlPreparedContext): Future[void] +) {.async.} = + if self.isInTransaction: + let ctx = MysqlPreparedContext(owner: self, connI: self.transactionConn) + await body(ctx) + return + + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + defer: + self.returnConn(connI).await + + let ctx = MysqlPreparedContext(owner: self, connI: connI) + await body(ctx) + + +proc verifyCtx(self: MysqlPreparedStatement, ctx: MysqlPreparedContext) = + self.mustBeOpen() + if ctx.isNil: + raise newException(DbError, "MySQL prepared context is nil") + if ctx.owner != self.owner: + raise newException(DbError, "MySQL prepared context owner mismatch") + if ctx.connI < 0 or ctx.connI >= self.owner.pools.conns.len: + raise newException(DbError, "MySQL prepared context has invalid connection index") + + +proc prepare*(self: MysqlConnections, sql: string): MysqlPreparedStatement = + new(result) + result.owner = self + result.info = self.info + result.entry = self.getStmtEntry(sql) + result.sql = sql + result.nArgs = result.entry.nArgs + result.entry.refCount += 1 + touchStmtEntry(result.entry) + result.resultBindCache = newSeq[MysqlResultBindCache](self.pools.conns.len) + + +proc ensurePreparedStmt(self: MysqlPreparedStatement, connI: int): Future[PSTMT] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "MySQL prepared statement received an invalid connection index") + if self.entry.stmts[connI].isNil: + self.entry.stmts[connI] = await mysql_impl.prepareStmt( + self.owner.pools.conns[connI].conn, + self.sql, + self.owner.pools.timeout + ) + touchStmtEntry(self.entry) + return self.entry.stmts[connI] + + +proc getPreparedRowsOnConn( + self: MysqlPreparedStatement, + connI: int, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "MySQL prepared statement received an invalid connection index") + + let stmt = await self.ensurePreparedStmt(connI) + if connI >= self.resultBindCache.len: + self.resultBindCache.setLen(connI + 1) + if self.resultBindCache[connI].isNil: + new(self.resultBindCache[connI]) + return mysql_impl.queryPreparedStmt( + self.owner.pools.conns[connI].conn, + stmt, + args, + self.owner.pools.timeout, + self.resultBindCache[connI] + ).await + + +proc getPreparedRows(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[(seq[seq[string]], DbRows)] {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + return await self.getPreparedRowsOnConn(connI, args) + + +proc getPreparedRows( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.verifyCtx(ctx) + return await self.getPreparedRowsOnConn(ctx.connI, args) + + +proc getPreparedAllRows(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getPreparedRow(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getPreparedAllRowsPlain(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getPreparedRows(args) + return rows + + +proc getPreparedRowPlain(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + let (rows, _) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc getPreparedAllRows( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getPreparedRow( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getPreparedAllRowsPlain( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getPreparedRows(ctx, args) + return rows + + +proc getPreparedRowPlain( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + let (rows, _) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc execPreparedOnConn( + self: MysqlPreparedStatement, + connI: int, + args: seq[PreparedParam] +) {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "MySQL prepared statement received an invalid connection index") + + let stmt = await self.ensurePreparedStmt(connI) + await mysql_impl.execPreparedStmt( + self.owner.pools.conns[connI].conn, + stmt, + args, + self.owner.pools.timeout + ) + + +proc execPrepared(self: MysqlPreparedStatement, args: seq[PreparedParam]) {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + await self.execPreparedOnConn(connI, args) + + +proc execPrepared( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +) {.async.} = + self.verifyCtx(ctx) + await self.execPreparedOnConn(ctx.connI, args) + + +proc preparedGet(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRows(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirst(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGetPlain(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRowsPlain(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirstPlain(self: MysqlPreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRowPlain(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedExec(self: MysqlPreparedStatement, args: seq[PreparedParam]) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGet( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRows(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirst( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGetPlain( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRowsPlain(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirstPlain( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRowPlain(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedExec( + self: MysqlPreparedStatement, + ctx: MysqlPreparedContext, + args: seq[PreparedParam] +) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc get*(self: MysqlPreparedStatement, args: seq[string]): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(args.toPreparedParams) + + +proc get*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: seq[string]): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(ctx, args.toPreparedParams) + + +proc get*(self: MysqlPreparedStatement, args: JsonNode): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(args.toPreparedParams) + + +proc get*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: JsonNode): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(ctx, args.toPreparedParams) + + +proc first*(self: MysqlPreparedStatement, args: seq[string]): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(args.toPreparedParams) + + +proc first*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: seq[string]): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(ctx, args.toPreparedParams) + + +proc first*(self: MysqlPreparedStatement, args: JsonNode): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(args.toPreparedParams) + + +proc first*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: JsonNode): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(ctx, args.toPreparedParams) + + +proc getPlain*(self: MysqlPreparedStatement, args: seq[string]): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(args.toPreparedParams) + + +proc getPlain*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: seq[string]): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(ctx, args.toPreparedParams) + + +proc getPlain*(self: MysqlPreparedStatement, args: JsonNode): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(args.toPreparedParams) + + +proc getPlain*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: JsonNode): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: MysqlPreparedStatement, args: seq[string]): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(args.toPreparedParams) + + +proc firstPlain*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: seq[string]): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: MysqlPreparedStatement, args: JsonNode): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(args.toPreparedParams) + + +proc firstPlain*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: JsonNode): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(ctx, args.toPreparedParams) + + +proc exec*(self: MysqlPreparedStatement, args: seq[string]) {.async.} = + await self.preparedExec(args.toPreparedParams) + + +proc exec*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: seq[string]) {.async.} = + await self.preparedExec(ctx, args.toPreparedParams) + + +proc exec*(self: MysqlPreparedStatement, args: JsonNode) {.async.} = + await self.preparedExec(args.toPreparedParams) + + +proc exec*(self: MysqlPreparedStatement, ctx: MysqlPreparedContext, args: JsonNode) {.async.} = + await self.preparedExec(ctx, args.toPreparedParams) + + +proc close*(self: MysqlPreparedStatement) {.async.} = + if self.isNil or self.isClosed: + return + self.isClosed = true + if not self.entry.isNil: + if self.entry.refCount > 0: + self.entry.refCount -= 1 + touchStmtEntry(self.entry) + + +proc flushStmt*(self: MysqlConnections, sql: string) {.async.} = + if not hasPreparedEntry(self.pools.preparedCache, sql): + return + let entry = self.pools.preparedCache[sql] + for i, stmt in entry.stmts: + if stmt.isNil: + continue + try: + mysql_impl.closePreparedStmt(stmt) + except CatchableError: + self.log.echoErrorMsg("close failed for prepared stmt: " & getCurrentExceptionMsg()) + entry.stmts[i] = nil + self.pools.preparedCache.del(sql) + + +proc clearStmtCache*(self: MysqlConnections) {.async.} = + let keys = toSeq(self.pools.preparedCache.keys) + for sql in keys: + await self.flushStmt(sql) + + +proc get*(self: RawMysqlQuery):Future[seq[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRows(self.queryString).await + + +proc getPlain*(self: RawMysqlQuery):Future[seq[seq[string]]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRowsPlain(self.queryString, self.placeHolder).await + + +proc exec*(self: RawMysqlQuery) {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + self.exec(self.queryString).await + + +proc first*(self: RawMysqlQuery):Future[Option[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRow(self.queryString).await + + +proc firstPlain*(self: RawMysqlQuery):Future[seq[string]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRowPlain(self.queryString, self.placeHolder).await + + +template seeder*(rdb:MysqlConnections, tableName:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table is empty. + block: + if rdb.table(tableName).count().waitFor == 0: + body + + +template seeder*(rdb:MysqlConnections, tableName, column:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table or specified column is empty. + block: + if rdb.table(tableName).select(column).count().waitFor == 0: + body diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_open.nim b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_open.nim new file mode 100644 index 0000000..76bc416 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_open.nim @@ -0,0 +1,89 @@ +import std/tables +import std/times +import ../../libs/database_url +import ../../libs/mysql/mysql_rdb +import ../../error +import ../../log +import ./mysql_types + + +proc dbOpen*(_:type MySQL, database: string, user: string, password: string, + host: string, port: int, maxConnections: int = 1, timeout=30, + shouldDisplayLog=false, shouldOutputLogFile=false, logDir="", + maxConnectionLifetime=DEFAULT_CONN_MAX_LIFETIME_SECONDS, + maxConnectionIdleTime=DEFAULT_CONN_MAX_IDLE_SECONDS): MysqlConnections = + var conns = newSeq[Connection](maxConnections) + for i in 0..=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"] +const whereSymbolsError = """Arg position 3 is only allowed of ["is", "is not", "=", "!=", "<", "<=", ">=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"]""" + +proc where*(self: MysqlQuery, column: string, symbol: string, + value: string|int|float): MysqlQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: MysqlQuery, column: string, symbol: string, + value: bool): MysqlQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: MysqlQuery, column: string, symbol: string, value: nil.type): MysqlQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["where_null"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orWhere*(self: MysqlQuery, column: string, symbol: string, + value: string|int|float|bool): MysqlQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["or_where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc orWhere*(self: MysqlQuery, column: string, symbol: string, value: nil.type): MysqlQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["or_where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "null" + }) + return self + + +proc whereBetween*(self:MysqlQuery, column:string, width:array[2, int|float]): MysqlQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_between") == false: + self.query["where_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereBetween*(self:MysqlQuery, column:string, width:array[2, string]): MysqlQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_between_string") == false: + self.query["where_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self:MysqlQuery, column:string, width:array[2, int|float]): MysqlQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_not_between") == false: + self.query["where_not_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self:MysqlQuery, column:string, width:array[2, string]): MysqlQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_not_between_string") == false: + self.query["where_not_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereIn*(self:MysqlQuery, column:string, width:seq[int|float|string]): MysqlQuery = + for row in width: + self.placeHolder.add(%*{"key": column, "value": row}) + + if self.query.hasKey("where_in") == false: + self.query["where_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotIn*(self:MysqlQuery, column:string, width:seq[int|float|string]): MysqlQuery = + for row in width: + self.placeHolder.add(%*{"key": column, "value": row}) + + if self.query.hasKey("where_not_in") == false: + self.query["where_not_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNull*(self:MysqlQuery, column:string): MysqlQuery = + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": "is", + }] + else: + self.query["where_null"].add(%*{ + "column": column, + "symbol": "is", + }) + return self + + +proc groupBy*(self:MysqlQuery, column:string): MysqlQuery = + if self.query.hasKey("group_by") == false: + self.query["group_by"] = %*[{"column": column}] + else: + self.query["group_by"].add(%*{"column": column}) + return self + + +proc having*(self: MysqlQuery, column: string, symbol: string, + value: string|int|float|bool): MysqlQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + +proc having*(self: MysqlQuery, column: string, symbol: string, value: nil.type): MysqlQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orderBy*(self:MysqlQuery, column:string, order:Order): MysqlQuery = + if self.query.hasKey("order_by") == false: + self.query["order_by"] = %*[{ + "column": column, + "order": $order + }] + else: + self.query["order_by"].add(%*{ + "column": column, + "order": $order + }) + return self + + +proc limit*(self: MysqlQuery, num: int): MysqlQuery = + self.query["limit"] = %num + return self + + +proc offset*(self: MysqlQuery, num: int): MysqlQuery = + self.query["offset"] = %num + return self + + +proc raw*(self:MysqlConnections, sql:string, arges=newJArray()): RawMysqlQuery = + ## arges is `JArray` `[true, 1, 1.1, "str"]` + ## + ## can't use BLOB data. + let rawQueryRdb = RawMysqlQuery( + log: self.log, + pools: self.pools, + info: self.info, + query: newJObject(), + queryString: sql.strip(), + placeHolder: arges, + isInTransaction: false, + transactionConn: 0 + ) + return rawQueryRdb diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_transaction.nim b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_transaction.nim new file mode 100644 index 0000000..94af6bc --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_transaction.nim @@ -0,0 +1,42 @@ +import std/macros +import std/strutils +import std/strformat +import ../../models/mysql/mysql_types + + +when NimMajor == 1: + macro rdbTransaction(rdb:MysqlConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except DbError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body +else: + macro rdbTransaction(rdb:MysqlConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except CatchableError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body + + +template transaction*(rdb:MysqlConnections, callback: untyped) = + rdbTransaction(rdb, callback) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_types.nim b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_types.nim new file mode 100644 index 0000000..8006e43 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mysql/mysql_types.nim @@ -0,0 +1,109 @@ +import std/tables +import std/json +import ../../log +import ../../libs/mysql/mysql_rdb + + +type MySQL* = object + + +const DEFAULT_CONN_MAX_LIFETIME_SECONDS* = 300 +const DEFAULT_CONN_MAX_IDLE_SECONDS* = 300 + + +type ConnectionInfo* = object + database*:string + user*:string + password*:string + host*:string + port*:int + + +type Connection* = object + conn*: PMySQL + isBusy*: bool + createdAt*: int64 + lastUsedAt*: int64 + + +type MysqlPreparedEntry* = ref object + sql*: string + nArgs*: int + stmts*: seq[PSTMT] + refCount*: int + lastUsedAt*: int64 + + +type Connections* = ref object + conns*: seq[Connection] + timeout*:int + maxConnectionLifetime*: int + maxConnectionIdleTime*: int + info*: ConnectionInfo + preparedCache*: Table[string, MysqlPreparedEntry] + + +## created by `let rdb = dbOpen(MySQL, "localhost", 3306)` +type MysqlConnections* = ref object + log*: LogSetting + pools*:Connections + info*:ConnectionInfo + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type MysqlPreparedContext* = ref object + owner*: MysqlConnections + connI*: int + + +## created by `rdb.select("columnName")` or `rdb.table("tableName")` +type MysqlQuery* = ref object + log*: LogSetting + pools*:Connections + info*:ConnectionInfo + query*: JsonNode + queryString*: string + placeHolder*: JsonNode # [{"key":"user", "value":"user1"}] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type RawMysqlQuery* = ref object + log*: LogSetting + pools*:Connections + info*:ConnectionInfo + query*: JsonNode + queryString*: string + placeHolder*: JsonNode # ["user1", "user1@example.com"] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type MysqlResultBindCache* = ref object + binds*: seq[BIND] + buffers*: seq[string] + lengths*: seq[culong] + nullFlags*: seq[my_bool] + errorFlags*: seq[my_bool] + + +type MysqlPreparedStatement* = ref object + owner*: MysqlConnections + info*: ConnectionInfo + entry*: MysqlPreparedEntry + sql*: string + nArgs*: int + isClosed*: bool + resultBindCache*: seq[MysqlResultBindCache] + + +proc `$`*(self:MysqlConnections|MysqlQuery|RawMysqlQuery):string = + return "MySQL" + + +proc isConnected*(self:MysqlConnections|MysqlQuery|RawMysqlQuery):bool = + return self.pools.conns.len > 0 diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_builder.nim b/clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_builder.nim new file mode 100644 index 0000000..8c4e962 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_builder.nim @@ -0,0 +1,228 @@ +import std/json +import ../mysql_types +import ./mysql_generator + + +# ==================== SELECT ==================== + +proc selectBuilder*(self: MysqlQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc selectFirstBuilder*(self: MysqlQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .selectFirstSql() + .offsetSql() + .queryString + +proc selectFindBuilder*(self: MysqlQuery, key: string): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .selectByIdSql(key) + .queryString + + +# ==================== INSERT ==================== + +proc insertValueBuilder*(self: MysqlQuery, items: JsonNode): string = + return self + .insertSql() + .insertValueSql(items) + .queryString + +proc insertValuesBuilder*(self: MysqlQuery, rows: openArray[JsonNode]): string = + return self + .insertSql() + .insertValuesSql(rows) + .queryString + + +# ==================== UPDATE ==================== + +proc updateBuilder*(self: MysqlQuery, items: JsonNode): string = + return self + .updateSql() + .updateValuesSql(items) + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + + +# ==================== DELETE ==================== + +proc deleteBuilder*(self: MysqlQuery): string = + return self + .deleteSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + +proc deleteByIdBuilder*(self: MysqlQuery, id: int, key: string): string = + return self + .deleteSql() + .fromSql() + .deleteByIdSql(id, key) + .queryString + + +# ==================== Aggregates ==================== + +proc countBuilder*(self:MysqlQuery): string = + return self + .selectCountSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc maxBuilder*(self:MysqlQuery, column:string): string = + return self + .selectMaxSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc minBuilder*(self:MysqlQuery, column:string): string = + return self + .selectMinSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc avgBuilder*(self:MysqlQuery, column:string): string = + return self + .selectAvgSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc sumBuilder*(self:MysqlQuery, column:string): string = + return self + .selectSumSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc columnBuilder*(self:MysqlQuery): string = + return self + .selectSql() + .fromSql() + .selectFirstSql() + .queryString diff --git a/clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_generator.nim b/clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_generator.nim new file mode 100644 index 0000000..db791de --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/mysql/query/mysql_generator.nim @@ -0,0 +1,430 @@ +import std/json +import std/strformat +import std/strutils +# import ../../database_types +import ../mysql_types + + +proc quote(input:string):string = + ## "User.id as userId" => ```User```.```id``` as ```userId``` + var tmp = newSeq[string]() + for segment in input.split("."): + if segment.contains(" as "): + # Split once on ' as ' to separate the expression and its alias + let parts = segment.split(" as ", maxsplit = 1) + let expression = parts[0] + let alias = parts[1] + if expression.contains("("): + # Extract function name and column name using index-based slicing + let funcStart = expression.find('(') + let funcEnd = expression.find(')', funcStart) + let funcName = expression[0 ..< funcStart] + let columnName = expression[funcStart + 1 ..< funcEnd] + tmp.add(&"{funcName}(`{columnName}`) as `{alias}`") + else: + # Quote the expression and alias + tmp.add(&"`{expression}` as `{alias}`") + elif segment.contains("("): + # Leave functions without alias as is + tmp.add(segment) + else: + # Quote standalone identifiers + tmp.add(&"`{segment}`") + return tmp.join(".") + + +# ==================== SELECT ==================== + +proc selectSql*(self: MysqlQuery): MysqlQuery = + var queryString = "" + + if self.query.hasKey("distinct"): + queryString.add("SELECT DISTINCT") + else: + queryString.add("SELECT") + + if self.query.hasKey("select"): + for i, item in self.query["select"].getElems(): + if i > 0: queryString.add(",") + var column = item.getStr() + # if column.contains("as"): + # let original = column.split("as")[0].strip() + # let renamed = column.split("as")[1].strip() + # queryString.add(&" `{original}` as `{renamed}`") + # else: + # queryString.add(&" `{column}`") + column = quote(column) + queryString.add(&" {column}") + else: + queryString.add(" *") + + self.queryString = queryString + return self + + +proc fromSql*(self: MysqlQuery): MysqlQuery = + let table = self.query["table"].getStr().quote() + self.queryString.add(&" FROM {table}") + return self + + +proc selectFirstSql*(self: MysqlQuery): MysqlQuery = + self.queryString.add(" LIMIT 1") + return self + + +proc selectByIdSql*(self: MysqlQuery, key:string): MysqlQuery = + let key = key.quote() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {key} = ? LIMIT 1") + else: + self.queryString.add(&" WHERE {key} = ? LIMIT 1") + return self + + +proc joinSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("join"): + for row in self.query["join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" INNER JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc leftJoinSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("left_join"): + for row in self.query["left_join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" LEFT JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc whereSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where"): + for i, row in self.query["where"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + if i == 0: + self.queryString.add(&" WHERE {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + return self + + +proc orWhereSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("or_where"): + for row in self.query["or_where"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" OR {column} {symbol} ?") + else: + self.queryString.add(&" WHERE {column} {symbol} ?") + return self + + +proc whereBetweenSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where_between"): + for row in self.query["where_between"]: + let column = row["column"].getStr() + # let start = row["width"][0].getFloat() + # let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` BETWEEN {start} AND {stop}") + self.queryString.add(&" AND `{column}` BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` BETWEEN {start} AND {stop}") + self.queryString.add(&" WHERE `{column}` BETWEEN ? AND ?") + return self + + +proc whereBetweenStringSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where_between_string"): + for row in self.query["where_between_string"]: + let column = row["column"].getStr() + # let start = row["width"][0].getStr + # let stop = row["width"][1].getStr + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" AND `{column}` BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" WHERE `{column}` BETWEEN ? AND ?") + return self + + +proc whereNotBetweenSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where_not_between"): + for row in self.query["where_not_between"]: + let column = row["column"].getStr().quote() + # let start = row["width"][0].getFloat() + # let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` NOT BETWEEN {start} AND {stop}") + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` NOT BETWEEN {start} AND {stop}") + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereNotBetweenStringSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where_not_between_string"): + for row in self.query["where_not_between_string"]: + let column = row["column"].getStr().quote() + # let start = row["width"][0].getStr + # let stop = row["width"][1].getStr + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND `{column}` NOT BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE `{column}` NOT BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereInSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where_in"): + var widthString = "" + for row in self.query["where_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} IN ({widthString})") + return self + + +proc whereNotInSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where_not_in"): + var widthString = "" + for row in self.query["where_not_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} NOT IN ({widthString})") + return self + + +proc whereNullSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("where_null"): + for row in self.query["where_null"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} {symbol} NULL") + else: + self.queryString.add(&" WHERE {column} {symbol} NULL") + return self + + +proc groupBySql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("group_by"): + for row in self.query["group_by"]: + let column = row["column"].getStr().quote() + if self.queryString.contains("GROUP BY"): + self.queryString.add(&", {column}") + else: + self.queryString.add(&" GROUP BY {column}") + return self + + +proc havingSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("having"): + for i, row in self.query["having"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + + if i == 0: + self.queryString.add(&" HAVING {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + + return self + + +proc orderBySql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("order_by"): + for row in self.query["order_by"]: + let column = row["column"].getStr().quote() + let order = row["order"].getStr() + + if self.queryString.contains("ORDER BY"): + self.queryString.add(&", {column} {order}") + else: + self.queryString.add(&" ORDER BY {column} {order}") + return self + + +proc limitSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("limit"): + let num = self.query["limit"].getInt() + self.queryString.add(&" LIMIT {num}") + + return self + + +proc offsetSql*(self: MysqlQuery): MysqlQuery = + if self.query.hasKey("offset"): + let num = self.query["offset"].getInt() + self.queryString.add(&" OFFSET {num}") + + return self + + +# ==================== INSERT ==================== + +proc insertSql*(self: MysqlQuery): MysqlQuery = + let table = self.query["table"].getStr() + self.queryString = &"INSERT INTO `{table}`" + return self + + +proc insertValueSql*(self: MysqlQuery, items: JsonNode): MysqlQuery = + var columns = "" + var values = "" + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: + columns.add(", ") + values.add(", ") + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"`{key}`") + + self.placeHolder.add(%*{"key":key, "value":val}) + values.add("?") + + self.queryString.add(&" ({columns}) VALUES ({values})") + return self + + +proc insertValuesSql*(self: MysqlQuery, rows: openArray[JsonNode]): MysqlQuery = + var columns = "" + + var i = 0 + for key, value in rows[0]: + defer: i += 1 + if i > 0: columns.add(", ") + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"`{key}`") + + var values = "" + var valuesCount = 0 + for items in rows: + var valueCount = 0 + var value = "" + for key, val in items.pairs: + defer: valueCount += 1 + if valueCount > 0: value.add(", ") + + self.placeHolder.add(%*{"key":key, "value":val}) + value.add("?") + + if valuesCount > 0: values.add(", ") + valuesCount += 1 + values.add(&"({value})") + + self.queryString.add(&" ({columns}) VALUES {values}") + return self + + +# ==================== UPDATE ==================== + +proc updateSql*(self: MysqlQuery): MysqlQuery = + var queryString = "" + queryString.add("UPDATE") + + var table = self.query["table"].getStr() + queryString.add(&" `{table}` SET") + self.queryString = queryString + return self + + +proc updateValuesSql*(self: MysqlQuery, items:JsonNode): MysqlQuery = + var value = "" + let placeHolder = newJArray() + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: value.add(",") + value.add(&" `{key}` = ?") + placeHolder.add(%*{"key":key, "value":val}) + + for row in self.placeHolder.items: + placeHolder.add(row) + + self.placeHolder = placeHolder + + self.queryString.add(value) + return self + + +# ==================== DELETE ==================== + +proc deleteSql*(self: MysqlQuery): MysqlQuery = + self.queryString = "DELETE" + return self + + +proc deleteByIdSql*(self: MysqlQuery, id: int, key: string): MysqlQuery = + self.queryString.add(&" WHERE `{key}` = ?") + return self + +# ==================== Aggregates ==================== + +proc selectCountSql*(self: MysqlQuery): MysqlQuery = + let queryString = + if self.query.hasKey("select"): + let column = self.query["select"][0].getStr + &"`{column}`" + else: + "*" + self.queryString = &"SELECT count({queryString}) as aggregate" + return self + + +proc selectMaxSql*(self: MysqlQuery, column:string): MysqlQuery = + self.queryString = &"SELECT max(`{column}`) as aggregate" + return self + + +proc selectMinSql*(self: MysqlQuery, column:string): MysqlQuery = + self.queryString = &"SELECT min(`{column}`) as aggregate" + return self + + +proc selectAvgSql*(self: MysqlQuery, column:string): MysqlQuery = + self.queryString = &"SELECT avg(`{column}`) as aggregate" + return self + + +proc selectSumSql*(self: MysqlQuery, column:string): MysqlQuery = + self.queryString = &"SELECT sum(`{column}`) as aggregate" + return self diff --git a/clients/nim-allographer/src/allographer/query_builder/models/orm.nim b/clients/nim-allographer/src/allographer/query_builder/models/orm.nim new file mode 100644 index 0000000..dbb96f3 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/orm.nim @@ -0,0 +1,18 @@ +import std/asyncdispatch +import std/options +import std/json + +proc orm*[T](response:Future[seq[JsonNode]], typ:typedesc[T]):Future[seq[T]] {.async.} = + let response = response.await + var result = newSeq[T](response.len) + for i, res in response: + result[i] = res.to(typ) + return result + + +proc orm*[T](response:Future[Option[JsonNode]], typ:typedesc[T]):Future[Option[T]] {.async.} = + let response = response.await + if response.isSome: + return response.get.to(typ).some() + else: + return none(T) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/postgres/poatgres_transaction.nim b/clients/nim-allographer/src/allographer/query_builder/models/postgres/poatgres_transaction.nim new file mode 100644 index 0000000..f8199a4 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/postgres/poatgres_transaction.nim @@ -0,0 +1,42 @@ +import std/macros +import std/strutils +import std/strformat +import ../../models/postgres/postgres_types + + +when NimMajor == 1: + macro rdbTransaction(rdb:PostgresConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except DbError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body +else: + macro rdbTransaction(rdb:PostgresConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except CatchableError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body + + +template transaction*(rdb:PostgresConnections, callback: untyped) = + rdbTransaction(rdb, callback) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_exec.nim b/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_exec.nim new file mode 100644 index 0000000..cdac361 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_exec.nim @@ -0,0 +1,1330 @@ +import std/asyncdispatch +import std/deques +import std/atomics +import std/json +import std/monotimes +import std/options +import std/strformat +import std/strutils +import std/sequtils +import std/tables +import std/times +import ../../error +import ../../libs/postgres/postgres_lib +import ../../libs/postgres/postgres_impl +import ../../libs/postgres/postgres_rdb +import ../../log +import ../database_types +import ../../prepared_param +import ./query/postgres_builder +import ./postgres_types + +var gPreparedStmtCounter: Atomic[int] + + +# ================================================================================ +# connection +# ================================================================================ + +proc removePoolWaiter(pools: Connections, w: Future[void]) = + var kept = initDeque[Future[void]]() + while pools.waiters.len > 0: + let x = pools.waiters.popFirst() + if x != w: + kept.addLast(x) + pools.waiters = move(kept) + +proc wakeOnePoolWaiter(pools: Connections) = + while pools.waiters.len > 0: + let w = pools.waiters.popFirst() + if w.finished: + continue + w.complete() + break + +proc poolRemainingMs(deadline: MonoTime): int = + let left = (deadline - getMonoTime()).inMilliseconds + if left <= 0: + return 0 + if left > int64(high(int)): + return high(int) + result = int(left) + if result < 1: + result = 1 + + +proc nowUnix(): int64 = + getTime().toUnix() + + +proc hasConnExpired(self: Connections, conn: Connection): bool = + let now = nowUnix() + if self.maxConnectionLifetime > 0 and now - conn.createdAt >= self.maxConnectionLifetime.int64: + return true + if self.maxConnectionIdleTime > 0 and now - conn.lastUsedAt >= self.maxConnectionIdleTime.int64: + return true + return false + + +proc openPostgresConn(self: Connections): PPGconn = + let conn = postgres_rdb.pqsetdbLogin( + self.host.cstring, + self.port.`$`.cstring, + nil, + nil, + self.database.cstring, + self.user.cstring, + self.password.cstring + ) + if pqStatus(conn) != CONNECTION_OK: + dbError(conn) + if pqsetnonblocking(conn, 1'i32) != 0'i32: + dbError(conn) + if pqisnonblocking(conn) != 1'i32: + raise newException(DbError, "PostgreSQL connection could not be set to non-blocking mode") + return conn + + +proc clearPreparedSlot(self: Connections, connI: int) = + for entry in self.preparedCache.values: + if connI < 0 or connI >= entry.stmtNames.len: + continue + entry.stmtNames[connI] = "" + + +proc refreshConn(self: Connections, connI: int): bool = + if connI < 0 or connI >= self.conns.len: + return false + let conn = openPostgresConn(self) + let oldConn = self.conns[connI].conn + self.clearPreparedSlot(connI) + if not oldConn.isNil: + pqfinish(oldConn) + self.conns[connI].conn = conn + self.conns[connI].createdAt = nowUnix() + self.conns[connI].lastUsedAt = self.conns[connI].createdAt + return true + +proc getFreeConn(self: PostgresConnections | PostgresQuery | RawPostgresQuery): Future[int] {.async.} = + let deadline = getMonoTime() + initDuration(seconds = self.pools.timeout) + while true: + for i in 0 ..< self.pools.conns.len: + if not self.pools.conns[i].isBusy: + self.pools.conns[i].isBusy = true + if self.pools.hasConnExpired(self.pools.conns[i]): + try: + discard self.pools.refreshConn(i) + except CatchableError: + discard + when defined(check_pool): + echo "=== getFreeConn ", i + return i + if getMonoTime() >= deadline: + return errorConnectionNum + let w = newFuture[void]("getFreeConn.poolWait") + self.pools.waiters.addLast(w) + var ms = poolRemainingMs(deadline) + if ms < 1: + ms = 1 + let ok = await withTimeout(w, ms) + if not ok: + removePoolWaiter(self.pools, w) + return errorConnectionNum + + +proc returnConn(self: PostgresConnections | PostgresQuery | RawPostgresQuery, i: int) {.async.} = + if i != errorConnectionNum: + self.pools.conns[i].isBusy = false + self.pools.conns[i].lastUsedAt = nowUnix() + wakeOnePoolWaiter(self.pools) + + +proc raisePoolTimeout(self: PostgresConnections | PostgresQuery | RawPostgresQuery | PostgresPreparedStatement) {.noreturn.} = + raise newException(DbError, "Timed out while waiting for a free PostgreSQL connection") + + +proc touchStmtEntry(entry: PostgresPreparedEntry) = + entry.lastUsedAt = getTime().toUnix() + + +proc mustBeOpen(self: PostgresPreparedStatement) = + if self.isNil or self.isClosed: + raise newException(DbError, "PostgreSQL prepared statement is already closed") + + +proc getStmtEntry(self: PostgresConnections, sql: string): PostgresPreparedEntry = + if self.pools.preparedCache.hasKey(sql): + return self.pools.preparedCache[sql] + let entry = PostgresPreparedEntry( + sql: sql, + nArgs: countQuestionMarks(sql), + stmtBaseName: &"allographer_stmt_{gPreparedStmtCounter.fetchAdd(1)}", + stmtNames: newSeq[string](self.pools.conns.len), + refCount: 0, + lastUsedAt: getTime().toUnix(), + ) + self.pools.preparedCache[sql] = entry + return entry + + +proc prepare*(self: PostgresConnections, sql: string): PostgresPreparedStatement = + let entry = self.getStmtEntry(sql) + entry.refCount += 1 + touchStmtEntry(entry) + new(result) + result.owner = self + result.entry = entry + result.sql = sql + result.nArgs = entry.nArgs + result.isClosed = false + + +proc ensureStmt(self: PostgresPreparedStatement, connI: int): Future[string] {.async.} = + self.mustBeOpen() + if self.entry.stmtNames[connI].len == 0: + let stmtName = &"{self.entry.stmtBaseName}_{connI}" + await postgres_impl.prepare( + self.owner.pools.conns[connI].conn, + self.sql, + self.owner.pools.timeout, + stmtName, + self.nArgs + ) + self.entry.stmtNames[connI] = stmtName + touchStmtEntry(self.entry) + return self.entry.stmtNames[connI] + + +# ================================================================================ +# toJson +# ================================================================================ + +proc toJson(results:openArray[seq[string]], dbRows:DbRows):seq[JsonNode] = + var response_table = newSeq[JsonNode](results.len) + for index, rows in results.pairs: + var response_row = newJObject() + for i, row in rows: + let key = dbRows[index][i].name + let typ = dbRows[index][i].typ.kind + # let kindName = dbRows[index][i].typ.name + # let size = dbRows[index][i].typ.size + + if typ == dbNull: + response_row[key] = newJNull() + elif [dbInt, dbUInt].contains(typ): + response_row[key] = newJInt(row.parseInt) + elif [dbDecimal, dbFloat].contains(typ): + response_row[key] = newJFloat(row.parseFloat) + elif [dbBool].contains(typ): + if row == "f": + response_row[key] = newJBool(false) + elif row == "t": + response_row[key] = newJBool(true) + elif [dbJson].contains(typ): + response_row[key] = row.parseJson + elif [dbFixedChar, dbVarchar].contains(typ): + if row == "NULL": + response_row[key] = newJNull() + else: + response_row[key] = newJString(row) + else: + response_row[key] = newJString(row) + + response_table[index] = response_row + return response_table + + +# ================================================================================ +# private exec +# ================================================================================ + +const pgInfoSchemaColumnsQuery = + "SELECT column_name, data_type FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = $1" + +proc getCachedColumnTypes(self: PostgresQuery, connI: int): Future[seq[Row]] {.async.} = + let table = self.query["table"].getStr + if self.pools.columnTypeCache.hasKey(table): + return self.pools.columnTypeCache[table] + let args = %*[%*{"key": "table", "value": table}] + let (columns, _) = postgres_impl.query( + self.pools.conns[connI].conn, pgInfoSchemaColumnsQuery, args, self.pools.timeout + ).await + self.pools.columnTypeCache[table] = columns + return columns + + +proc getAllRows(self:PostgresQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = postgres_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:PostgresQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = postgres_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + return rows + + +proc getRow(self:PostgresQuery, queryString:string, connI:int=0):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, dbRows) = postgres_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some # seq[JsonNode] + + +proc getRowPlain(self:PostgresQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let (rows, _) = postgres_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + return rows[0] + + +proc exec(self:PostgresQuery, queryString:string) {.async.} = + ## args is `JObject` + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let columns = getCachedColumnTypes(self, connI).await + postgres_impl.exec(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + + +proc insertId(self:PostgresQuery, queryString:string, key:string):Future[string] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let columns = getCachedColumnTypes(self, connI).await + let (rows, _) = postgres_impl.execGetValue(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + return rows[0][0] + + +proc getAllRows(self:RawPostgresQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let queryString = queryString.questionToDaller() + + let (rows, dbRows) = postgres_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:RawPostgresQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let queryString = queryString.questionToDaller() + + let (rows, _) = postgres_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + return rows + + +proc getRow(self:RawPostgresQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let queryString = queryString.questionToDaller() + + let (rows, dbRows) = postgres_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some # seq[JsonNode] + + +proc getRowPlain(self:RawPostgresQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let queryString = queryString.questionToDaller() + + let (rows, _) = postgres_impl.rawQuery( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + return rows[0] + + +proc exec(self:RawPostgresQuery, queryString:string) {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let queryString = queryString.questionToDaller() + + postgres_impl.rawExec( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + +proc getColumn(self:PostgresQuery, queryString:string):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + return postgres_impl.getColumns(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + +proc transactionStart(self:PostgresConnections|PostgresQuery) {.async.} = + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + self.isInTransaction = true + self.transactionConn = connI + + postgres_impl.exec(self.pools.conns[connI].conn, "BEGIN", newJArray(), newSeq[seq[string]](), self.pools.timeout).await + + +proc transactionEnd(self:PostgresConnections|PostgresQuery, query:string) {.async.} = + postgres_impl.exec(self.pools.conns[self.transactionConn].conn, query, newJArray(), newSeq[seq[string]](), self.pools.timeout).await + self.returnConn(self.transactionConn).await + self.transactionConn = 0 + self.isInTransaction = false + + +proc withConn*( + self: PostgresConnections, + body: proc (ctx: PostgresPreparedContext): Future[void] +) {.async.} = + if self.isInTransaction: + let ctx = PostgresPreparedContext(owner: self, connI: self.transactionConn) + await body(ctx) + return + + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + defer: + self.returnConn(connI).await + + let ctx = PostgresPreparedContext(owner: self, connI: connI) + await body(ctx) + + +proc verifyCtx(self: PostgresPreparedStatement, ctx: PostgresPreparedContext) = + self.mustBeOpen() + if ctx.isNil: + raise newException(DbError, "PostgreSQL prepared context is nil") + if ctx.owner != self.owner: + raise newException(DbError, "PostgreSQL prepared context owner mismatch") + if ctx.connI < 0 or ctx.connI >= self.owner.pools.conns.len: + raise newException(DbError, "PostgreSQL prepared context has invalid connection index") + + +proc getRowsOnConn( + self: PostgresPreparedStatement, + connI: int, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "PostgreSQL prepared statement received an invalid connection index") + let stmtName = await self.ensureStmt(connI) + return postgres_impl.preparedQuery( + self.owner.pools.conns[connI].conn, + args, + self.nArgs, + self.owner.pools.timeout, + stmtName + ).await + + +proc getRows(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[(seq[seq[string]], DbRows)] {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + return await self.getRowsOnConn(connI, args) + + +proc getRows( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.verifyCtx(ctx) + return await self.getRowsOnConn(ctx.connI, args) + + +proc getAll(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getAll( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getOne(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getOne( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getAllPlain(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getRows(args) + return rows + + +proc getAllPlain( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getRows(ctx, args) + return rows + + +proc getPlainRow(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + let (rows, _) = await self.getRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc getPlainRow( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + let (rows, _) = await self.getRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc runExecOnConn( + self: PostgresPreparedStatement, + connI: int, + args: seq[PreparedParam] +) {.async.} + + +proc runExec(self: PostgresPreparedStatement, args: seq[PreparedParam]) {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + await self.runExecOnConn(connI, args) + + +proc runExecOnConn( + self: PostgresPreparedStatement, + connI: int, + args: seq[PreparedParam] +) {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "PostgreSQL prepared statement received an invalid connection index") + + let stmtName = await self.ensureStmt(connI) + await postgres_impl.preparedExec( + self.owner.pools.conns[connI].conn, + args, + self.nArgs, + self.owner.pools.timeout, + stmtName + ) + + +proc runExec( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +) {.async.} = + self.verifyCtx(ctx) + await self.runExecOnConn(ctx.connI, args) + + +proc get(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getAll(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc get( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getAll(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc first(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getOne(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc first( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getOne(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc getPlain(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getAllPlain(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc getPlain( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getAllPlain(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc firstPlain(self: PostgresPreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPlainRow(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc firstPlain( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPlainRow(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc exec(self: PostgresPreparedStatement, args: seq[PreparedParam]) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.runExec(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc exec( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[PreparedParam] +) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.runExec(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc get*(self: PostgresPreparedStatement, args: seq[string]): Future[seq[JsonNode]] {.async.} = + return await self.get(args.toPreparedParams) + + +proc get*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[string] +): Future[seq[JsonNode]] {.async.} = + return await self.get(ctx, args.toPreparedParams) + + +proc get*(self: PostgresPreparedStatement, args: JsonNode): Future[seq[JsonNode]] {.async.} = + return await self.get(args.toPreparedParams) + + +proc get*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: JsonNode +): Future[seq[JsonNode]] {.async.} = + return await self.get(ctx, args.toPreparedParams) + + +proc first*(self: PostgresPreparedStatement, args: seq[string]): Future[Option[JsonNode]] {.async.} = + return await self.first(args.toPreparedParams) + + +proc first*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[string] +): Future[Option[JsonNode]] {.async.} = + return await self.first(ctx, args.toPreparedParams) + + +proc first*(self: PostgresPreparedStatement, args: JsonNode): Future[Option[JsonNode]] {.async.} = + return await self.first(args.toPreparedParams) + + +proc first*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: JsonNode +): Future[Option[JsonNode]] {.async.} = + return await self.first(ctx, args.toPreparedParams) + + +proc getPlain*(self: PostgresPreparedStatement, args: seq[string]): Future[seq[seq[string]]] {.async.} = + return await self.getPlain(args.toPreparedParams) + + +proc getPlain*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[string] +): Future[seq[seq[string]]] {.async.} = + return await self.getPlain(ctx, args.toPreparedParams) + + +proc getPlain*(self: PostgresPreparedStatement, args: JsonNode): Future[seq[seq[string]]] {.async.} = + return await self.getPlain(args.toPreparedParams) + + +proc getPlain*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: JsonNode +): Future[seq[seq[string]]] {.async.} = + return await self.getPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: PostgresPreparedStatement, args: seq[string]): Future[seq[string]] {.async.} = + return await self.firstPlain(args.toPreparedParams) + + +proc firstPlain*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[string] +): Future[seq[string]] {.async.} = + return await self.firstPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: PostgresPreparedStatement, args: JsonNode): Future[seq[string]] {.async.} = + return await self.firstPlain(args.toPreparedParams) + + +proc firstPlain*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: JsonNode +): Future[seq[string]] {.async.} = + return await self.firstPlain(ctx, args.toPreparedParams) + + +proc exec*(self: PostgresPreparedStatement, args: seq[string]) {.async.} = + await self.exec(args.toPreparedParams) + + +proc exec*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: seq[string] +) {.async.} = + await self.exec(ctx, args.toPreparedParams) + + +proc exec*(self: PostgresPreparedStatement, args: JsonNode) {.async.} = + await self.exec(args.toPreparedParams) + + +proc exec*( + self: PostgresPreparedStatement, + ctx: PostgresPreparedContext, + args: JsonNode +) {.async.} = + await self.exec(ctx, args.toPreparedParams) + + +# ================================================================================ +# public exec +# ================================================================================ + +# ==================== return json ==================== +proc get*(self: PostgresQuery):Future[seq[JsonNode]] {.async.} = + var sql = self.selectBuilder() + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getAllRows(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc first*(self: PostgresQuery):Future[Option[JsonNode]] {.async.} = + var sql = self.selectFirstBuilder() + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self: PostgresQuery, id:string, key="id"):Future[Option[JsonNode]] {.async.} = + self.placeHolder.add(%*{"key":key, "value": id}) + var sql = self.selectFindBuilder(key) + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self: PostgresQuery, id:int, key="id"):Future[Option[JsonNode]] {.async.} = + return self.find($id, key).await + + +# ==================== return string ==================== +proc getPlain*(self:PostgresQuery):Future[seq[seq[string]]] {.async.} = + var sql = self.selectBuilder() + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getAllRowsPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc firstPlain*(self:PostgresQuery):Future[seq[string]] {.async.} = + var sql = self.selectFirstBuilder() + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:PostgresQuery, id: string, key="id"):Future[seq[string]] {.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + var sql = self.selectFindBuilder(key) + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:PostgresQuery, id: int, key="id"):Future[seq[string]] {.async.} = + return self.findPlain($id, key).await + + +# ==================== insert JsonNode ==================== +proc insert*(self:PostgresQuery, items:JsonNode) {.async.} = + ## items is `JObject` + var sql = self.insertValueBuilder(items) + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*(self:PostgresQuery, items:seq[JsonNode]) {.async.} = + var sql = self.insertValuesBuilder(items) + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*(self:PostgresQuery, items:JsonNode, key="id"):Future[string] {.async.} = + var sql = self.insertValueBuilder(items) + sql.add(&" RETURNING \"{key}\"") + sql = questionToDaller(sql) + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*(self: PostgresQuery, items: seq[JsonNode], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + var sql = self.insertValueBuilder(item) + sql.add(&" RETURNING \"{key}\"") + sql = questionToDaller(sql) + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +# ==================== insert Object ==================== +proc insert*[T](self:PostgresQuery, items:T) {.async.} = + var sql = self.insertValueBuilder(%items) + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*[T](self:PostgresQuery, items:seq[T]) {.async.} = + let items = items.mapIt(%it) + var sql = self.insertValuesBuilder(items) + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*[T](self:PostgresQuery, items:T, key="id"):Future[string] {.async.} = + var sql = self.insertValueBuilder(%items) + sql.add(&" RETURNING \"{key}\"") + sql = questionToDaller(sql) + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*[T](self: PostgresQuery, items: seq[T], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + var sql = self.insertValueBuilder(%item) + sql.add(&" RETURNING \"{key}\"") + sql = questionToDaller(sql) + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +# ==================== update ==================== +proc update*(self: PostgresQuery, items: JsonNode){.async.} = + var sql = self.updateBuilder(items) + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc update*[T](self: PostgresQuery, items: T){.async.} = + var sql = self.updateBuilder(%items) + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self: PostgresQuery){.async.} = + var sql = self.deleteBuilder() + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self: PostgresQuery, id: int, key="id"){.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + var sql = self.deleteByIdBuilder(id, key) + sql = questionToDaller(sql) + self.log.logger(sql) + self.exec(sql).await + + +proc columns*(self:PostgresQuery):Future[seq[string]] {.async.} = + ## get columns sequence from table + var sql = self.columnBuilder() + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getColumn(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc count*(self:PostgresQuery):Future[int] {.async.} = + var sql = self.countBuilder() + sql = questionToDaller(sql) + self.log.logger(sql) + let response = self.getRow(sql).await + + if response.isSome: + return response.get["aggregate"].getInt() + else: + return 0 + + +proc min*(self:PostgresQuery, column:string):Future[Option[string]] {.async.} = + var sql = self.minBuilder(column) + sql = questionToDaller(sql) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc max*(self:PostgresQuery, column:string):Future[Option[string]] {.async.} = + var sql = self.maxBuilder(column) + sql = questionToDaller(sql) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc avg*(self:PostgresQuery, column:string):Future[Option[float]]{.async.} = + var sql = self.avgBuilder(column) + sql = questionToDaller(sql) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getFloat().some + else: + return none(float) + + +proc sum*(self:PostgresQuery, column:string):Future[Option[float]]{.async.} = + var sql = self.sumBuilder(column) + sql = questionToDaller(sql) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getFloat().some + else: + return none(float) + + +proc begin*(self:PostgresConnections) {.async.} = + self.log.logger("BEGIN") + self.transactionStart().await + + +proc rollback*(self:PostgresConnections) {.async.} = + self.log.logger("ROLLBACK") + self.transactionEnd("ROLLBACK").await + + +proc commit*(self:PostgresConnections) {.async.} = + self.log.logger("COMMIT") + self.transactionEnd("COMMIT").await + + +proc get*(self: RawPostgresQuery):Future[seq[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRows(self.queryString).await + + +proc getPlain*(self: RawPostgresQuery):Future[seq[seq[string]]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRowsPlain(self.queryString, self.placeHolder).await + + +proc exec*(self: RawPostgresQuery) {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + self.exec(self.queryString).await + + +proc first*(self: RawPostgresQuery):Future[Option[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRow(self.queryString).await + + +proc firstPlain*(self: RawPostgresQuery):Future[seq[string]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRowPlain(self.queryString, self.placeHolder).await + + +proc deallocStmtSafely( + self: PostgresConnections, + connI: int, + stmtName: string +): Future[void] {.async.} = + try: + await postgres_impl.deallocate(self.pools.conns[connI].conn, stmtName, self.pools.timeout) + except CatchableError: + self.log.echoErrorMsg("deallocate failed for " & stmtName & ": " & getCurrentExceptionMsg()) + + +proc close*(self: PostgresPreparedStatement) {.async.} = + if self.isNil or self.isClosed: + return + self.isClosed = true + if not self.entry.isNil: + if self.entry.refCount > 0: + self.entry.refCount -= 1 + touchStmtEntry(self.entry) + + +proc flushStmt*(self: PostgresConnections, sql: string) {.async.} = + if not self.pools.preparedCache.hasKey(sql): + return + let entry = self.pools.preparedCache[sql] + var futs: seq[Future[void]] + for i, stmtName in entry.stmtNames: + if stmtName.len == 0: + continue + futs.add(self.deallocStmtSafely(i, stmtName)) + if futs.len > 0: + await all(futs) + self.pools.preparedCache.del(sql) + + +proc clearStmtCache*(self: PostgresConnections) {.async.} = + let keys = toSeq(self.pools.preparedCache.keys) + for sql in keys: + await self.flushStmt(sql) + + +template seeder*(rdb:PostgresConnections, tableName:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table is empty. + block: + if rdb.table(tableName).count().waitFor == 0: + `body` + + +template seeder*(rdb:PostgresConnections, tableName, column:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table or specified column is empty. + block: + if rdb.table(tableName).select(column).count().waitFor == 0: + `body` diff --git a/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_open.nim b/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_open.nim new file mode 100644 index 0000000..8d18de3 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_open.nim @@ -0,0 +1,82 @@ +import std/asyncdispatch +import std/deques +import std/tables +import std/times +import ../database_types +import ../../error +import ../../libs/database_url +import ../../libs/postgres/postgres_rdb +import ../../libs/postgres/postgres_lib +import ../../log +import ./postgres_types + +proc dbOpen*(_:type PostgreSQL, database: string, user: string, password: string, + host: string, port: int, maxConnections=1, timeout=30, + shouldDisplayLog=false, shouldOutputLogFile=false, logDir="", + maxConnectionLifetime=DEFAULT_CONN_MAX_LIFETIME_SECONDS, + maxConnectionIdleTime=DEFAULT_CONN_MAX_IDLE_SECONDS): PostgresConnections = + var conns = newSeq[Connection](maxConnections) + for i in 0..=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"] +const whereSymbolsError = """Arg position 3 is only allowed of ["is", "is not", "=", "!=", "<", "<=", ">=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"]""" + +proc where*(self: PostgresQuery, column: string, symbol: string, + value: string|int|float): PostgresQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: PostgresQuery, column: string, symbol: string, + value: bool): PostgresQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: PostgresQuery, column: string, symbol: string, value: nil.type): PostgresQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["where_null"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orWhere*(self: PostgresQuery, column: string, symbol: string, + value: string|int|float|bool): PostgresQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["or_where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc orWhere*(self: PostgresQuery, column: string, symbol: string, value: nil.type): PostgresQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["or_where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "null" + }) + return self + + +proc whereBetween*(self:PostgresQuery, column:string, width:array[2, int|float]): PostgresQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_between") == false: + self.query["where_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereBetween*(self:PostgresQuery, column:string, width:array[2, string]): PostgresQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_between_string") == false: + self.query["where_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self:PostgresQuery, column:string, width:array[2, int|float]): PostgresQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_not_between") == false: + self.query["where_not_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotBetween*(self:PostgresQuery, column:string, width:array[2, string]): PostgresQuery = + self.placeHolder.add(%*{"key": column, "value": width[0]}) + self.placeHolder.add(%*{"key": column, "value": width[1]}) + + if self.query.hasKey("where_not_between_string") == false: + self.query["where_not_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereIn*(self:PostgresQuery, column:string, width:seq[int|float|string]): PostgresQuery = + for row in width: + self.placeHolder.add(%*{"key": column, "value": row}) + + if self.query.hasKey("where_in") == false: + self.query["where_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotIn*(self:PostgresQuery, column:string, width:seq[int|float|string]): PostgresQuery = + for row in width: + self.placeHolder.add(%*{"key": column, "value": row}) + + if self.query.hasKey("where_not_in") == false: + self.query["where_not_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNull*(self:PostgresQuery, column:string): PostgresQuery = + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": "is", + }] + else: + self.query["where_null"].add(%*{ + "column": column, + "symbol": "is", + }) + return self + + +proc groupBy*(self:PostgresQuery, column:string): PostgresQuery = + if self.query.hasKey("group_by") == false: + self.query["group_by"] = %*[{"column": column}] + else: + self.query["group_by"].add(%*{"column": column}) + return self + + +proc having*(self: PostgresQuery, column: string, symbol: string, + value: string|int|float|bool): PostgresQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + +proc having*(self: PostgresQuery, column: string, symbol: string, value: nil.type): PostgresQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orderBy*(self:PostgresQuery, column:string, order:Order): PostgresQuery = + if self.query.hasKey("order_by") == false: + self.query["order_by"] = %*[{ + "column": column, + "order": $order + }] + else: + self.query["order_by"].add(%*{ + "column": column, + "order": $order + }) + return self + + +proc limit*(self: PostgresQuery, num: int): PostgresQuery = + self.query["limit"] = %num + return self + + +proc offset*(self: PostgresQuery, num: int): PostgresQuery = + self.query["offset"] = %num + return self + + +proc raw*(self:PostgresConnections, sql:string, arges=newJArray()): RawPostgresQuery = + ## arges is `JArray` + ## + ## can't use BLOB data. + let rawQueryRdb = RawPostgresQuery( + log: self.log, + pools: self.pools, + query: newJObject(), + queryString: sql, + placeHolder: arges, + isInTransaction: false, + transactionConn: 0 + ) + return rawQueryRdb diff --git a/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_types.nim b/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_types.nim new file mode 100644 index 0000000..4f89aa6 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/postgres/postgres_types.nim @@ -0,0 +1,101 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/tables +import ../database_types +import ../../log +import ../../libs/postgres/postgres_rdb + + +type PostgreSQL* = object + + +const DEFAULT_CONN_MAX_LIFETIME_SECONDS* = 300 +const DEFAULT_CONN_MAX_IDLE_SECONDS* = 300 + + +type Connection* = ref object + conn*: PPGconn + isBusy*: bool + createdAt*: int64 + lastUsedAt*: int64 + + +type PostgresPreparedEntry* = ref object + sql*: string + nArgs*: int + stmtBaseName*: string + stmtNames*: seq[string] + refCount*: int + lastUsedAt*: int64 + + +type Connections* = ref object + conns*: seq[Connection] + timeout*: int + maxConnectionLifetime*: int + maxConnectionIdleTime*: int + database*: string + user*: string + password*: string + host*: string + port*: int + ## `getFreeConn` が接続を待つときに積む Future。`returnConn` が先頭から 1 件だけ完了させる。 + waiters*: Deque[Future[void]] + ## `exec` / `insertId` 用。テーブルごとに information_schema 相当の列型を初回のみ取得して保持する。 + columnTypeCache*: Table[string, seq[Row]] + ## SQL 単位の prepared statement cache。物理 prepare 状態は conn ごとに保持する。 + preparedCache*: Table[string, PostgresPreparedEntry] + + +## created by `let rdb = dbOpen(PostgreSQL, "localhost", 5432)` +type PostgresConnections* = ref object + log*: LogSetting + pools*:Connections + # for transaction + isInTransaction*: bool + transactionConn*: int + + +## created by `rdb.select("columnName")` or `rdb.table("tableName")` +type PostgresQuery* = ref object + log*: LogSetting + pools*:Connections + query*: JsonNode # JObject + queryString*: string + placeHolder*: JsonNode # JArray [{"key":"user", "value":"user1"}] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type RawPostgresQuery* = ref object + log*: LogSetting + pools*:Connections + query*: JsonNode + queryString*: string + placeHolder*: JsonNode # JArray ["user1", "user1@example.com"] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type PostgresPreparedContext* = ref object + owner*: PostgresConnections + connI*: int + + +type PostgresPreparedStatement* = ref object + owner*: PostgresConnections + entry*: PostgresPreparedEntry + sql*: string + nArgs*: int + isClosed*: bool + + +proc `$`*(self:PostgresConnections|PostgresQuery|RawPostgresQuery):string = + return "PostgreSQL" + + +proc isConnected*(self:PostgresConnections|PostgresQuery|RawPostgresQuery):bool = + return self.pools.conns.len > 0 diff --git a/clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_builder.nim b/clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_builder.nim new file mode 100644 index 0000000..edf1306 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_builder.nim @@ -0,0 +1,228 @@ +import std/json +import ../postgres_types +import ./postgres_generator + + +# ==================== SELECT ==================== + +proc selectBuilder*(self: PostgresQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc selectFirstBuilder*(self: PostgresQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .selectFirstSql() + .offsetSql() + .queryString + +proc selectFindBuilder*(self: PostgresQuery, key: string): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .selectByIdSql(key) + .queryString + + +# ==================== INSERT ==================== + +proc insertValueBuilder*(self: PostgresQuery, items: JsonNode): string = + return self + .insertSql() + .insertValueSql(items) + .queryString + +proc insertValuesBuilder*(self: PostgresQuery, rows: openArray[JsonNode]): string = + return self + .insertSql() + .insertValuesSql(rows) + .queryString + + +# ==================== UPDATE ==================== + +proc updateBuilder*(self: PostgresQuery, items: JsonNode): string = + return self + .updateSql() + .updateValuesSql(items) + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + + +# ==================== DELETE ==================== + +proc deleteBuilder*(self: PostgresQuery): string = + return self + .deleteSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + +proc deleteByIdBuilder*(self: PostgresQuery, id: int, key: string): string = + return self + .deleteSql() + .fromSql() + .deleteByIdSql(id, key) + .queryString + + +# ==================== Aggregates ==================== + +proc countBuilder*(self:PostgresQuery): string = + return self + .selectCountSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc maxBuilder*(self:PostgresQuery, column:string): string = + return self + .selectMaxSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc minBuilder*(self:PostgresQuery, column:string): string = + return self + .selectMinSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc avgBuilder*(self:PostgresQuery, column:string): string = + return self + .selectAvgSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc sumBuilder*(self:PostgresQuery, column:string): string = + return self + .selectSumSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc columnBuilder*(self:PostgresQuery): string = + return self + .selectSql() + .fromSql() + .selectFirstSql() + .queryString diff --git a/clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_generator.nim b/clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_generator.nim new file mode 100644 index 0000000..ef98773 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/postgres/query/postgres_generator.nim @@ -0,0 +1,429 @@ +import std/json +import std/strformat +import std/strutils +import ../postgres_types + + +proc quote(input:string):string = + ## `User.id as userId` => `"User"."id" as "userId"` + ## + ## `COUNT(id) as count` => `COUNT("id") as "count"` + ## + ## `MAX(id) as maxId` => `MAX("id") as "maxId"` + ## + + var tmp = newSeq[string]() + for segment in input.split("."): + if segment.contains(" as "): + # Split once on ' as ' to separate the expression and its alias + let parts = segment.split(" as ", maxsplit = 1) + let expression = parts[0] + let alias = parts[1] + if expression.contains("("): + # Extract function name and column name using index-based slicing + let funcStart = expression.find('(') + let funcEnd = expression.find(')', funcStart) + let funcName = expression[0 ..< funcStart] + let columnName = expression[funcStart + 1 ..< funcEnd] + tmp.add(&"{funcName}(\"{columnName}\") as \"{alias}\"") + else: + # Quote the expression and alias + tmp.add(&"\"{expression}\" as \"{alias}\"") + elif segment.contains("("): + # Leave functions without alias as is + tmp.add(segment) + else: + # Quote standalone identifiers + tmp.add(&"\"{segment}\"") + return tmp.join(".") + + +# ==================== SELECT ==================== + +proc selectSql*(self: PostgresQuery): PostgresQuery = + var queryString = "" + + if self.query.hasKey("distinct"): + queryString.add("SELECT DISTINCT") + else: + queryString.add("SELECT") + + if self.query.hasKey("select"): + for i, item in self.query["select"].getElems(): + if i > 0: queryString.add(",") + var column = item.getStr() + if column != "*": column = quote(column) + queryString.add(&" {column}") + else: + queryString.add(" *") + + self.queryString = queryString + return self + + +proc fromSql*(self: PostgresQuery): PostgresQuery = + let table = self.query["table"].getStr() + self.queryString.add(&" FROM \"{table}\"") + return self + + +proc selectFirstSql*(self: PostgresQuery): PostgresQuery = + self.queryString.add(" LIMIT 1") + return self + + +proc selectByIdSql*(self: PostgresQuery, key:string): PostgresQuery = + let key = key.quote() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {key} = ? LIMIT 1") + else: + self.queryString.add(&" WHERE {key} = ? LIMIT 1") + return self + + +proc joinSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("join"): + for row in self.query["join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" INNER JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc leftJoinSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("left_join"): + for row in self.query["left_join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" LEFT JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc whereSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where"): + for i, row in self.query["where"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + if i == 0: + self.queryString.add(&" WHERE {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + return self + + +proc orWhereSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("or_where"): + for row in self.query["or_where"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" OR {column} {symbol} ?") + else: + self.queryString.add(&" WHERE {column} {symbol} ?") + return self + + +proc whereBetweenSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where_between"): + for row in self.query["where_between"]: + let column = row["column"].getStr() + # let start = row["width"][0].getFloat() + # let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND \"{column}\" BETWEEN {start} AND {stop}") + self.queryString.add(&" AND \"{column}\" BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE \"{column}\" BETWEEN {start} AND {stop}") + self.queryString.add(&" WHERE \"{column}\" BETWEEN ? AND ?") + return self + + +proc whereBetweenStringSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where_between_string"): + for row in self.query["where_between_string"]: + let column = row["column"].getStr() + # let start = row["width"][0].getStr + # let stop = row["width"][1].getStr + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND \"{column}\" BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" AND \"{column}\" BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE \"{column}\" BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" WHERE \"{column}\" BETWEEN ? AND ?") + return self + + +proc whereNotBetweenSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where_not_between"): + for row in self.query["where_not_between"]: + let column = row["column"].getStr().quote() + # let start = row["width"][0].getFloat() + # let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND \"{column}\" NOT BETWEEN {start} AND {stop}") + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE \"{column}\" NOT BETWEEN {start} AND {stop}") + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereNotBetweenStringSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where_not_between_string"): + for row in self.query["where_not_between_string"]: + let column = row["column"].getStr().quote() + # let start = row["width"][0].getStr + # let stop = row["width"][1].getStr + + if self.queryString.contains("WHERE"): + # self.queryString.add(&" AND \"{column}\" NOT BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" AND {column} NOT BETWEEN ? AND ?") + else: + # self.queryString.add(&" WHERE \"{column}\" NOT BETWEEN '{start}' AND '{stop}'") + self.queryString.add(&" WHERE {column} NOT BETWEEN ? AND ?") + return self + + +proc whereInSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where_in"): + var widthString = "" + for row in self.query["where_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} IN ({widthString})") + return self + + +proc whereNotInSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where_not_in"): + var widthString = "" + for row in self.query["where_not_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + widthString.add("?") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} NOT IN ({widthString})") + return self + + +proc whereNullSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("where_null"): + for row in self.query["where_null"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} {symbol} null") + else: + self.queryString.add(&" WHERE {column} {symbol} null") + return self + + +proc groupBySql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("group_by"): + for row in self.query["group_by"]: + let column = row["column"].getStr().quote() + if self.queryString.contains("GROUP BY"): + self.queryString.add(&", {column}") + else: + self.queryString.add(&" GROUP BY {column}") + return self + + +proc havingSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("having"): + for i, row in self.query["having"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + # let value = row["value"].getStr() + + if i == 0: + self.queryString.add(&" HAVING {column} {symbol} ?") + else: + self.queryString.add(&" AND {column} {symbol} ?") + + return self + + +proc orderBySql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("order_by"): + for row in self.query["order_by"]: + let column = row["column"].getStr().quote() + let order = row["order"].getStr() + + if self.queryString.contains("ORDER BY"): + self.queryString.add(&", {column} {order}") + else: + self.queryString.add(&" ORDER BY {column} {order}") + return self + + +proc limitSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("limit"): + let num = self.query["limit"].getInt() + self.queryString.add(&" LIMIT {num}") + + return self + + +proc offsetSql*(self: PostgresQuery): PostgresQuery = + if self.query.hasKey("offset"): + let num = self.query["offset"].getInt() + self.queryString.add(&" OFFSET {num}") + + return self + + +# ==================== INSERT ==================== + +proc insertSql*(self: PostgresQuery): PostgresQuery = + let table = self.query["table"].getStr() + self.queryString = &"INSERT INTO \"{table}\"" + return self + + +proc insertValueSql*(self: PostgresQuery, items: JsonNode): PostgresQuery = + var columns = "" + var values = "" + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: + columns.add(", ") + values.add(", ") + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"\"{key}\"") + + self.placeHolder.add(%*{"key":key, "value":val}) + values.add("?") + + self.queryString.add(&" ({columns}) VALUES ({values})") + return self + + +proc insertValuesSql*(self: PostgresQuery, rows: openArray[JsonNode]): PostgresQuery = + var columns = "" + + var i = 0 + for key, value in rows[0]: + defer: i += 1 + if i > 0: columns.add(", ") + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"\"{key}\"") + + var values = "" + var valuesCount = 0 + for items in rows: + var valueCount = 0 + var value = "" + for key, val in items.pairs: + defer: valueCount += 1 + if valueCount > 0: value.add(", ") + + self.placeHolder.add(%*{"key":key, "value":val}) + value.add("?") + + if valuesCount > 0: values.add(", ") + valuesCount += 1 + values.add(&"({value})") + + self.queryString.add(&" ({columns}) VALUES {values}") + return self + + +# ==================== UPDATE ==================== + +proc updateSql*(self: PostgresQuery): PostgresQuery = + var queryString = "" + queryString.add("UPDATE") + + var table = self.query["table"].getStr() + queryString.add(&" \"{table}\" SET") + self.queryString = queryString + return self + + +proc updateValuesSql*(self: PostgresQuery, items:JsonNode): PostgresQuery = + var value = "" + let placeHolder = newJArray() + + var i = 0 + for key, val in items.pairs: + defer: i += 1 + if i > 0: value.add(",") + value.add(&" \"{key}\" = ?") + placeHolder.add(%*{"key":key, "value":val}) + + for row in self.placeHolder.items: + placeHolder.add(row) + + self.placeHolder = placeHolder + + self.queryString.add(value) + return self + + +# ==================== DELETE ==================== + +proc deleteSql*(self: PostgresQuery): PostgresQuery = + self.queryString = "DELETE" + return self + + +proc deleteByIdSql*(self: PostgresQuery, id: int, key: string): PostgresQuery = + self.queryString.add(&" WHERE \"{key}\" = ?") + return self + +# ==================== Aggregates ==================== + +proc selectCountSql*(self: PostgresQuery): PostgresQuery = + let queryString = + if self.query.hasKey("select"): + let column = self.query["select"][0].getStr + &"\"{column}\"" + else: + "*" + self.queryString = &"SELECT count({queryString}) as aggregate" + return self + + +proc selectMaxSql*(self: PostgresQuery, column:string): PostgresQuery = + self.queryString = &"SELECT max(\"{column}\") as aggregate" + return self + + +proc selectMinSql*(self: PostgresQuery, column:string): PostgresQuery = + self.queryString = &"SELECT min(\"{column}\") as aggregate" + return self + + +proc selectAvgSql*(self: PostgresQuery, column:string): PostgresQuery = + self.queryString = &"SELECT avg(\"{column}\") as aggregate" + return self + + +proc selectSumSql*(self: PostgresQuery, column:string): PostgresQuery = + self.queryString = &"SELECT sum(\"{column}\") as aggregate" + return self diff --git a/clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_builder.nim b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_builder.nim new file mode 100644 index 0000000..776028d --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_builder.nim @@ -0,0 +1,229 @@ +import std/json +import ../sqlite_types +import ./sqlite_generator + + +# ==================== SELECT ==================== + +proc selectBuilder*(self: SqliteQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc selectFirstBuilder*(self: SqliteQuery): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .selectFirstSql() + .offsetSql() + .queryString + +proc selectFindBuilder*(self: SqliteQuery, key: string): string = + return self + .selectSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereBetweenStringSql() + .whereNotBetweenSql() + .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .selectByIdSql(key) + .queryString + + +# ==================== INSERT ==================== + +proc insertValueBuilder*(self: SqliteQuery, items: JsonNode): string = + ## items is JOject + return self + .insertSql() + .insertValueSql(items) + .queryString + +proc insertValuesBuilder*(self: SqliteQuery, rows: openArray[JsonNode]): string = + return self + .insertSql() + .insertValuesSql(rows) + .queryString + + +# ==================== UPDATE ==================== + +proc updateBuilder*(self: SqliteQuery, items: JsonNode): string = + return self + .updateSql() + .updateValuesSql(items) + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + + +# ==================== DELETE ==================== + +proc deleteBuilder*(self: SqliteQuery): string = + return self + .deleteSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .limitSql() + .offsetSql() + .queryString + +proc deleteByIdBuilder*(self: SqliteQuery, id: int, key: string): string = + return self + .deleteSql() + .fromSql() + .deleteByIdSql(id, key) + .queryString + + +# ==================== Aggregates ==================== + +proc countBuilder*(self:SqliteQuery): string = + return self + .selectCountSql() + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc maxBuilder*(self:SqliteQuery, column:string): string = + return self + .selectMaxSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc minBuilder*(self:SqliteQuery, column:string): string = + return self + .selectMinSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc avgBuilder*(self:SqliteQuery, column:string): string = + return self + .selectAvgSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc sumBuilder*(self:SqliteQuery, column:string): string = + return self + .selectSumSql(column) + .fromSql() + .joinSql() + .leftJoinSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + .havingSql() + .orderBySql() + .limitSql() + .offsetSql() + .queryString + +proc columnBuilder*(self:SqliteQuery): string = + return self + .selectSql() + .fromSql() + .selectFirstSql() + .queryString diff --git a/clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_generator.nim b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_generator.nim new file mode 100644 index 0000000..88551ba --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/query/sqlite_generator.nim @@ -0,0 +1,431 @@ +import std/json +import std/strformat +import std/strutils +# import ../../database_types +import ../sqlite_types + + +proc quote(input:string):string = + ## `User.id as userId` => `"User"."id" as "userId"` + ## + ## `COUNT(id) as count` => `COUNT("id") as "count"` + ## + ## `MAX(id) as maxId` => `MAX("id") as "maxId"` + ## + var tmp = newSeq[string]() + for segment in input.split("."): + if segment.contains(" as "): + # Split once on ' as ' to separate the expression and its alias + let parts = segment.split(" as ", maxsplit = 1) + let expression = parts[0] + let alias = parts[1] + if expression.contains("("): + # Extract function name and column name using index-based slicing + let funcStart = expression.find('(') + let funcEnd = expression.find(')', funcStart) + let funcName = expression[0 ..< funcStart] + let columnName = expression[funcStart + 1 ..< funcEnd] + tmp.add(&"{funcName}(\"{columnName}\") as \"{alias}\"") + else: + # Quote the expression and alias + tmp.add(&"\"{expression}\" as \"{alias}\"") + elif segment.contains("("): + # Leave functions without alias as is + tmp.add(segment) + else: + # Quote standalone identifiers + tmp.add(&"\"{segment}\"") + return tmp.join(".") + +# ==================== SELECT ==================== + +proc selectSql*(self: SqliteQuery): SqliteQuery = + var queryString = "" + + if self.query.hasKey("distinct"): + queryString.add("SELECT DISTINCT") + else: + queryString.add("SELECT") + + if self.query.hasKey("select"): + for i, item in self.query["select"].getElems(): + if i > 0: queryString.add(",") + var column = item.getStr() + if column != "*": column = quote(column) + queryString.add(&" {column}") + else: + queryString.add(" *") + + self.queryString = queryString + return self + + +proc fromSql*(self: SqliteQuery): SqliteQuery = + let table = self.query["table"].getStr() + self.queryString.add(&" FROM \"{table}\"") + return self + + +proc selectFirstSql*(self: SqliteQuery): SqliteQuery = + self.queryString.add(" LIMIT 1") + return self + + +proc selectByIdSql*(self: SqliteQuery, key:string): SqliteQuery = + let key = key.quote() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {key} = ? LIMIT 1") + else: + self.queryString.add(&" WHERE {key} = ? LIMIT 1") + return self + + +proc joinSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("join"): + for row in self.query["join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" INNER JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc leftJoinSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("left_join"): + for row in self.query["left_join"]: + let table = row["table"].getStr().quote() + let column1 = row["column1"].getStr().quote() + let symbol = row["symbol"].getStr() + let column2 = row["column2"].getStr().quote() + + self.queryString.add(&" LEFT JOIN {table} ON {column1} {symbol} {column2}") + return self + + +proc whereSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where"): + for i, row in self.query["where"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + let value = row["value"].getStr() + + if i == 0: + self.queryString.add(&" WHERE {column} {symbol} {value}") + else: + self.queryString.add(&" AND {column} {symbol} {value}") + return self + + +proc orWhereSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("or_where"): + for row in self.query["or_where"]: + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + let value = row["value"].getStr() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" OR {column} {symbol} {value}") + else: + self.queryString.add(&" WHERE {column} {symbol} {value}") + return self + + +proc whereBetweenSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where_between"): + for row in self.query["where_between"]: + let column = row["column"].getStr().quote() + let start = row["width"][0].getFloat() + let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} BETWEEN {start} AND {stop}") + else: + self.queryString.add(&" WHERE {column} BETWEEN {start} AND {stop}") + return self + + +proc whereBetweenStringSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where_between_string"): + for row in self.query["where_between_string"]: + let column = row["column"].getStr().quote() + let start = row["width"][0].getStr() + let stop = row["width"][1].getStr() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} BETWEEN \"{start}\" AND \"{stop}\"") + else: + self.queryString.add(&" WHERE {column} BETWEEN \"{start}\" AND \"{stop}\"") + return self + + +proc whereNotBetweenSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where_not_between"): + for row in self.query["where_not_between"]: + let column = row["column"].getStr().quote() + let start = row["width"][0].getFloat() + let stop = row["width"][1].getFloat() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT BETWEEN {start} AND {stop}") + else: + self.queryString.add(&" WHERE {column} NOT BETWEEN {start} AND {stop}") + return self + + +proc whereNotBetweenStringSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where_not_between_string"): + for row in self.query["where_not_between_string"]: + let column = row["column"].getStr().quote() + let start = row["width"][0].getStr() + let stop = row["width"][1].getStr() + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT BETWEEN \"{start}\" AND \"{stop}\"") + else: + self.queryString.add(&" WHERE {column} NOT BETWEEN \"{start}\" AND \"{stop}\"") + return self + + +proc whereInSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where_in"): + var widthString = "" + for row in self.query["where_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + if val.kind == JInt: + widthString.add($(val.getInt())) + elif val.kind == JFloat: + widthString.add($(val.getFloat())) + elif val.kind == JString: + widthString.add(&"\"{val.getStr()}\"") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} IN ({widthString})") + return self + + +proc whereNotInSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where_not_in"): + var widthString = "" + for row in self.query["where_not_in"]: + let column = row["column"].getStr().quote() + for i, val in row["width"].getElems(): + if i > 0: widthString.add(", ") + if val.kind == JInt: + widthString.add($(val.getInt())) + elif val.kind == JFloat: + widthString.add($(val.getFloat())) + elif val.kind == JString: + widthString.add(&"\"{val.getStr()}\"") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} NOT IN ({widthString})") + else: + self.queryString.add(&" WHERE {column} NOT IN ({widthString})") + return self + + +proc whereNullSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("where_null"): + for row in self.query["where_null"]: + let column = row["column"].getStr().quote() + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {column} is null") + else: + self.queryString.add(&" WHERE {column} is null") + return self + + +proc groupBySql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("group_by"): + for row in self.query["group_by"]: + let column = row["column"].getStr().quote() + if self.queryString.contains("GROUP BY"): + self.queryString.add(&", {column}") + else: + self.queryString.add(&" GROUP BY {column}") + return self + + +proc havingSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("having"): + for i, row in self.query["having"].getElems(): + let column = row["column"].getStr().quote() + let symbol = row["symbol"].getStr() + let value = row["value"].getStr() + + if i == 0: + self.queryString.add(&" HAVING {column} {symbol} {value}") + else: + self.queryString.add(&" AND {column} {symbol} {value}") + + return self + + +proc orderBySql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("order_by"): + for row in self.query["order_by"]: + let column = row["column"].getStr().quote() + let order = row["order"].getStr() + + if self.queryString.contains("ORDER BY"): + self.queryString.add(&", {column} {order}") + else: + self.queryString.add(&" ORDER BY {column} {order}") + return self + + +proc limitSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("limit"): + let num = self.query["limit"].getInt() + self.queryString.add(&" LIMIT {num}") + + return self + + +proc offsetSql*(self: SqliteQuery): SqliteQuery = + if self.query.hasKey("offset"): + let num = self.query["offset"].getInt() + self.queryString.add(&" OFFSET {num}") + + return self + + +# ==================== INSERT ==================== + +proc insertSql*(self: SqliteQuery): SqliteQuery = + let table = self.query["table"].getStr() + self.queryString = &"INSERT INTO \"{table}\"" + return self + + +proc insertValueSql*(self: SqliteQuery, items: JsonNode): SqliteQuery = + ## items is JObject + var columns = "" + var values = "" + + var i = 0 + for key, val in items.pairs: + if i > 0: + columns.add(", ") + values.add(", ") + i += 1 + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"\"{key}\"") + + self.placeHolder.add(%*{"key":key, "value":val}) + values.add("?") + + self.queryString.add(&" ({columns}) VALUES ({values})") + return self + + +proc insertValuesSql*(self: SqliteQuery, rows: openArray[JsonNode]): SqliteQuery = + var columns = "" + + var i = 0 + for key, value in rows[0]: + defer: i += 1 + if i > 0: columns.add(", ") + # If column name contains Upper letter, column name is covered by double quote + columns.add(&"\"{key}\"") + + var values = "" + var valuesCount = 0 + for items in rows: + var valueCount = 0 + var value = "" + for key, val in items.pairs: + defer: valueCount += 1 + if valueCount > 0: value.add(", ") + + self.placeHolder.add(%*{"key":key, "value":val}) + value.add("?") + + if valuesCount > 0: values.add(", ") + valuesCount += 1 + values.add(&"({value})") + + self.queryString.add(&" ({columns}) VALUES {values}") + return self + + +# ==================== UPDATE ==================== + +proc updateSql*(self: SqliteQuery): SqliteQuery = + var queryString = "" + queryString.add("UPDATE") + + var table = self.query["table"].getStr() + queryString.add(&" \"{table}\" SET") + self.queryString = queryString + return self + + +proc updateValuesSql*(self: SqliteQuery, items:JsonNode): SqliteQuery = + var value = "" + let placeHolder = newJArray() + + var i = 0 + for key, val in items.pairs: + defer: i.inc() + if i > 0: value.add(",") + value.add(&" \"{key}\" = ?") + placeHolder.add(%*{"key":key, "value":val}) + + for row in self.placeHolder.items: + placeHolder.add(row) + + self.placeHolder = placeHolder + + self.queryString.add(value) + return self + + +# ==================== DELETE ==================== + +proc deleteSql*(self: SqliteQuery): SqliteQuery = + self.queryString = "DELETE" + return self + + +proc deleteByIdSql*(self: SqliteQuery, id: int, key: string): SqliteQuery = + self.queryString.add(&" WHERE \"{key}\" = ?") + return self + +# ==================== Aggregates ==================== + +proc selectCountSql*(self: SqliteQuery): SqliteQuery = + let queryString = + if self.query.hasKey("select"): + let column = self.query["select"][0].getStr + &"{column}" + else: + "*" + self.queryString = &"SELECT count({queryString}) as aggregate" + return self + + +proc selectMaxSql*(self: SqliteQuery, column:string): SqliteQuery = + self.queryString = &"SELECT max({column}) as aggregate" + return self + + +proc selectMinSql*(self: SqliteQuery, column:string): SqliteQuery = + self.queryString = &"SELECT min({column}) as aggregate" + return self + + +proc selectAvgSql*(self: SqliteQuery, column:string): SqliteQuery = + self.queryString = &"SELECT avg({column}) as aggregate" + return self + + +proc selectSumSql*(self: SqliteQuery, column:string): SqliteQuery = + self.queryString = &"SELECT sum({column}) as aggregate" + return self diff --git a/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_exec.nim b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_exec.nim new file mode 100644 index 0000000..74b80c3 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_exec.nim @@ -0,0 +1,1336 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/monotimes +import std/options +import std/strutils +import std/sequtils +import std/tables +import std/times +import ../../error +import ../../libs/sqlite/sqlite_impl +import ../../libs/sqlite/sqlite_lib +import ../../libs/sqlite/sqlite_rdb +import ../../log +import ../database_types +import ../../prepared_param +import ./query/sqlite_builder +import ./sqlite_types + + +# ================================================================================ +# connection +# ================================================================================ + +proc removePoolWaiter(pools: Connections, w: Future[void]) = + var kept = initDeque[Future[void]]() + while pools.waiters.len > 0: + let x = pools.waiters.popFirst() + if x != w: + kept.addLast(x) + pools.waiters = move(kept) + +proc wakeOnePoolWaiter(pools: Connections) = + while pools.waiters.len > 0: + let w = pools.waiters.popFirst() + if w.finished: + continue + w.complete() + break + +proc poolRemainingMs(deadline: MonoTime): int = + let left = (deadline - getMonoTime()).inMilliseconds + if left <= 0: + return 0 + if left > int64(high(int)): + return high(int) + result = int(left) + if result < 1: + result = 1 + + +proc nowUnix(): int64 = + getTime().toUnix() + + +proc hasConnExpired(self: Connections, conn: Connection): bool = + let now = nowUnix() + if self.maxConnectionLifetime > 0 and now - conn.createdAt >= self.maxConnectionLifetime.int64: + return true + if self.maxConnectionIdleTime > 0 and now - conn.lastUsedAt >= self.maxConnectionIdleTime.int64: + return true + return false + + +proc openSqliteConn(self: Connections): PSqlite3 = + var db: PSqlite3 + discard sqlite_rdb.open(self.database.cstring, db) + if db.isNil: + raise newException(DbError, "SQLite connection could not be opened") + return db + + +proc clearPreparedSlot(self: Connections, connI: int) = + for entry in self.preparedCache.values: + if connI < 0 or connI >= entry.stmts.len: + continue + if not entry.stmts[connI].isNil: + discard finalize(entry.stmts[connI]) + entry.stmts[connI] = nil + + +proc refreshConn(self: Connections, connI: int): bool = + if connI < 0 or connI >= self.conns.len: + return false + let db = openSqliteConn(self) + let oldConn = self.conns[connI].conn + self.clearPreparedSlot(connI) + if not oldConn.isNil: + discard sqlite_rdb.close(oldConn) + self.conns[connI].conn = db + self.conns[connI].createdAt = nowUnix() + self.conns[connI].lastUsedAt = self.conns[connI].createdAt + return true + +proc getFreeConn(self: SqliteConnections | SqliteQuery | RawSqliteQuery): Future[int] {.async.} = + let deadline = getMonoTime() + initDuration(seconds = self.pools.timeout) + while true: + for i in 0 ..< self.pools.conns.len: + if not self.pools.conns[i].isBusy: + self.pools.conns[i].isBusy = true + if self.pools.hasConnExpired(self.pools.conns[i]): + try: + discard self.pools.refreshConn(i) + except CatchableError: + discard + when defined(check_pool): + echo "=== getFreeConn ", i + return i + if getMonoTime() >= deadline: + return errorConnectionNum + let w = newFuture[void]("getFreeConn.poolWait") + self.pools.waiters.addLast(w) + var ms = poolRemainingMs(deadline) + if ms < 1: + ms = 1 + let ok = await withTimeout(w, ms) + if not ok: + removePoolWaiter(self.pools, w) + return errorConnectionNum + + +proc returnConn(self: SqliteConnections | SqliteQuery | RawSqliteQuery, i: int) {.async.} = + if i != errorConnectionNum: + self.pools.conns[i].isBusy = false + self.pools.conns[i].lastUsedAt = nowUnix() + wakeOnePoolWaiter(self.pools) + + +proc raisePoolTimeout(self: SqliteConnections | SqliteQuery | RawSqliteQuery | SqlitePreparedStatement) {.noreturn.} = + raise newException(DbError, "Timed out while waiting for a free SQLite connection") + + +proc touchStmtEntry(entry: SqlitePreparedEntry) = + entry.lastUsedAt = getTime().toUnix() + + +proc mustBeOpen(self: SqlitePreparedStatement) = + if self.isNil or self.isClosed: + raise newException(DbError, "SQLite prepared statement is already closed") + + +proc hasPreparedEntry(cache: Table[string, SqlitePreparedEntry], sql: string): bool = + for key in cache.keys: + if key == sql: + return true + return false + + +proc getStmtEntry(self: SqliteConnections, sql: string): SqlitePreparedEntry = + if hasPreparedEntry(self.pools.preparedCache, sql): + return self.pools.preparedCache[sql] + let entry = SqlitePreparedEntry( + sql: sql, + nArgs: countQuestionMarks(sql), + stmts: newSeq[PStmt](self.pools.conns.len), + refCount: 0, + lastUsedAt: getTime().toUnix(), + ) + self.pools.preparedCache[sql] = entry + return entry + + +proc prepare*(self: SqliteConnections, sql: string): SqlitePreparedStatement = + new(result) + result.owner = self + result.entry = self.getStmtEntry(sql) + result.sql = sql + result.entry.refCount += 1 + touchStmtEntry(result.entry) + + +proc ensurePreparedStmt(self: SqlitePreparedStatement, connI: int): Future[PStmt] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "SQLite prepared statement received an invalid connection index") + if self.entry.stmts[connI].isNil: + self.entry.stmts[connI] = sqlite_impl.prepare( + self.owner.pools.conns[connI].conn, + self.sql, + self.owner.pools.timeout + ).await + touchStmtEntry(self.entry) + return self.entry.stmts[connI] + + +proc withConn*( + self: SqliteConnections, + body: proc (ctx: SqlitePreparedContext): Future[void] +) {.async.} = + if self.isInTransaction: + let ctx = SqlitePreparedContext(owner: self, connI: self.transactionConn) + await body(ctx) + return + + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + defer: + self.returnConn(connI).await + + let ctx = SqlitePreparedContext(owner: self, connI: connI) + await body(ctx) + + +proc verifyCtx(self: SqlitePreparedStatement, ctx: SqlitePreparedContext) = + self.mustBeOpen() + if ctx.isNil: + raise newException(DbError, "SQLite prepared context is nil") + if ctx.owner != self.owner: + raise newException(DbError, "SQLite prepared context owner mismatch") + if ctx.connI < 0 or ctx.connI >= self.owner.pools.conns.len: + raise newException(DbError, "SQLite prepared context has invalid connection index") + + + +# ================================================================================ +# toJson +# ================================================================================ + +proc toJson(results:openArray[seq[string]], dbRows:DbRows):seq[JsonNode] = + var response_table = newSeq[JsonNode](results.len) + for index, rows in results.pairs: + var response_row = newJObject() + for i, row in rows: + let key = dbRows[index][i].name + let typ = dbRows[index][i].typ.kind + let kindName = dbRows[index][i].typ.name + # let size = dbRows[index][i].typ.size + + if typ == dbNull: + response_row[key] = newJNull() + elif ["INTEGER", "INT", "SMALLINT", "MEDIUMINT", "BIGINT"].contains(kindName): + response_row[key] = newJInt(row.parseInt) + elif ["NUMERIC", "DECIMAL", "DOUBLE", "REAL"].contains(kindName): + response_row[key] = newJFloat(row.parseFloat) + elif ["TINYINT", "BOOLEAN"].contains(kindName): + response_row[key] = newJBool(row.parseBool) + else: + response_row[key] = newJString(row) + + response_table[index] = response_row + return response_table + + +# ================================================================================ +# private exec +# ================================================================================ + +proc getCachedSqliteColumnTypes(self: SqliteQuery, connI: int): Future[seq[(string, string)]] {.async.} = + let table = self.query["table"].getStr + if self.pools.columnTypeCache.hasKey(table): + return self.pools.columnTypeCache[table] + let q = "PRAGMA table_info(" & sqliteQuoteIdent(table) & ")" + let columns = sqlite_impl.getColumnTypes(self.pools.conns[connI].conn, q).await + self.pools.columnTypeCache[table] = columns + return columns + + +proc getAllRows(self:SqliteQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + let (rows, dbRows) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:SqliteQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in args.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + let (rows, _) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + return rows + + +proc getRow(self:SqliteQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + let (rows, dbRows) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() # Option[JsonNode] + + +proc getRowPlain(self:SqliteQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in args.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + let (rows, _) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + return rows[0] + + +proc getAllRows(self:RawSqliteQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg.kind + of JBool: + if arg.getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg.getInt) + of JFloat: + strArgs.add($arg.getFloat) + of JString: + strArgs.add($arg.getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg.pretty) + + let (rows, dbRows) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) # seq[JsonNode] + + +proc getAllRowsPlain(self:RawSqliteQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in args.items: + case arg.kind + of JBool: + if arg.getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg.getInt) + of JFloat: + strArgs.add($arg.getFloat) + of JString: + strArgs.add($arg.getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg.pretty) + + let (rows, _) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + return rows + + +proc getRow(self:RawSqliteQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg.kind + of JBool: + if arg.getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg.getInt) + of JFloat: + strArgs.add($arg.getFloat) + of JString: + strArgs.add($arg.getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg.pretty) + + let (rows, dbRows) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return none(JsonNode) + return toJson(rows, dbRows)[0].some + + +proc getRowPlain(self:RawSqliteQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in args.items: + case arg.kind + of JBool: + if arg.getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg.getInt) + of JFloat: + strArgs.add($arg.getFloat) + of JString: + strArgs.add($arg.getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg.pretty) + + let (rows, _) = sqlite_impl.query(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + return rows[0] + + +proc exec(self:SqliteQuery, queryString:string) {.async.} = + ## args is `self.placeholder` + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let columns = getCachedSqliteColumnTypes(self, connI).await + sqlite_impl.exec(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + + +proc exec(self:RawSqliteQuery, queryString:string, args:JsonNode) {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + sqlite_impl.exec(self.pools.conns[connI].conn, queryString, args, self.pools.timeout).await + + +proc insertId(self:SqliteQuery, queryString:string, key:string):Future[string]{.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + let columns = getCachedSqliteColumnTypes(self, connI).await + sqlite_impl.exec(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + let (rows, _) = sqlite_impl.query(self.pools.conns[connI].conn, "SELECT last_insert_rowid()", strArgs, self.pools.timeout).await + return rows[0][0] + + +proc getColumns(self:SqliteQuery, queryString:string, args=newJArray()):Future[seq[string]] {.async.} = + var connI = self.transactionConn + if not self.isInTransaction: + connI = getFreeConn(self).await + defer: + if not self.isInTransaction: + self.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + var strArgs:seq[string] + for arg in args.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + return sqlite_impl.getColumns(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + +proc transactionStart(self:SqliteConnections) {.async.} = + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + self.isInTransaction = true + self.transactionConn = connI + sqlite_impl.exec(self.pools.conns[connI].conn, "BEGIN", newJArray(), self.pools.timeout).await + + +proc transactionEnd(self:SqliteConnections, query:string) {.async.} = + defer: + self.returnConn(self.transactionConn).await + self.transactionConn = 0 + self.isInTransaction = false + + sqlite_impl.exec(self.pools.conns[self.transactionConn].conn, query, newJArray(), self.pools.timeout).await + + +proc getPreparedRowsOnConn( + self: SqlitePreparedStatement, + connI: int, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "SQLite prepared statement received an invalid connection index") + + let stmt = await self.ensurePreparedStmt(connI) + if not self.hasCachedColumns: + setColumnsStaticMeta(self.cachedColumns, stmt) + self.hasCachedColumns = true + + return sqlite_impl.preparedQueryReuse( + self.owner.pools.conns[connI].conn, + stmt, + args, + self.owner.pools.timeout, + self.cachedColumns + ).await + + +proc getPreparedRows(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[(seq[seq[string]], DbRows)] {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + return await self.getPreparedRowsOnConn(connI, args) + + +proc getPreparedRows( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[(seq[seq[string]], DbRows)] {.async.} = + self.verifyCtx(ctx) + return await self.getPreparedRowsOnConn(ctx.connI, args) + + +proc getPreparedAllRows(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getPreparedRow(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getPreparedAllRowsPlain(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getPreparedRows(args) + return rows + + +proc getPreparedRowPlain(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + let (rows, _) = await self.getPreparedRows(args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc getPreparedAllRows( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[JsonNode](0) + return toJson(rows, dbRows) + + +proc getPreparedRow( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + let (rows, dbRows) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return none(JsonNode) + return toJson(rows, dbRows)[0].some() + + +proc getPreparedAllRowsPlain( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + let (rows, _) = await self.getPreparedRows(ctx, args) + return rows + + +proc getPreparedRowPlain( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + let (rows, _) = await self.getPreparedRows(ctx, args) + if rows.len == 0: + self.owner.log.echoErrorMsg(self.sql) + return newSeq[string](0) + return rows[0] + + +proc execPreparedOnConn( + self: SqlitePreparedStatement, + connI: int, + args: seq[PreparedParam] +) {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "SQLite prepared statement received an invalid connection index") + + let stmt = await self.ensurePreparedStmt(connI) + await sqlite_impl.preparedExecReuse( + self.owner.pools.conns[connI].conn, + stmt, + args, + self.owner.pools.timeout + ) + + +proc execPrepared(self: SqlitePreparedStatement, args: seq[PreparedParam]) {.async.} = + var connI = self.owner.transactionConn + if not self.owner.isInTransaction: + connI = getFreeConn(self.owner).await + defer: + if not self.owner.isInTransaction: + self.owner.returnConn(connI).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + + await self.execPreparedOnConn(connI, args) + + +proc execPrepared( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +) {.async.} = + self.verifyCtx(ctx) + await self.execPreparedOnConn(ctx.connI, args) + + +proc preparedGet(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRows(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirst(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGetPlain(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRowsPlain(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirstPlain(self: SqlitePreparedStatement, args: seq[PreparedParam]): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRowPlain(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedExec(self: SqlitePreparedStatement, args: seq[PreparedParam]) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGet( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRows(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirst( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedGetPlain( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[seq[seq[string]]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedAllRowsPlain(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedFirstPlain( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +): Future[seq[string]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRowPlain(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc preparedExec( + self: SqlitePreparedStatement, + ctx: SqlitePreparedContext, + args: seq[PreparedParam] +) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +# ================================================================================ +# public exec +# ================================================================================ + +# ==================== return json ==================== +proc get*(self:SqliteQuery):Future[seq[JsonNode]] {.async.} = + let sql = self.selectBuilder() + try: + self.log.logger(sql) + return self.getAllRows(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc first*(self:SqliteQuery):Future[Option[JsonNode]] {.async.} = + let sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self:SqliteQuery, id: string, key="id"):Future[Option[JsonNode]] {.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + let sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc find*(self:SqliteQuery, id:int, key="id"):Future[Option[JsonNode]]{.async.} = + return self.find($id, key).await + + +# ==================== return string ==================== +proc getPlain*(self:SqliteQuery):Future[seq[seq[string]]] {.async.} = + let sql = self.selectBuilder() + try: + self.log.logger(sql) + return self.getAllRowsPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc firstPlain*(self:SqliteQuery):Future[seq[string]] {.async.} = + let sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:SqliteQuery, id: string, key="id"):Future[seq[string]] {.async.} = + self.placeHolder.add(%*{"key":key, "value":id}) + let sql = self.selectFindBuilder(key) + try: + self.log.logger(sql) + return self.getRowPlain(sql, self.placeHolder).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +proc findPlain*(self:SqliteQuery, id: int, key="id"):Future[seq[string]] {.async.} = + return self.findPlain($id, key).await + + +# ==================== insert JsonNode ==================== +proc insert*(self:SqliteQuery, items:JsonNode) {.async.} = + let sql = self.insertValueBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*(self:SqliteQuery, items:seq[JsonNode]) {.async.} = + let sql = self.insertValuesBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*(self: SqliteQuery, items: JsonNode, key="id"):Future[string] {.async.} = + let sql = self.insertValueBuilder(items) + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*(self: SqliteQuery, items: seq[JsonNode], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + let sql = self.insertValueBuilder(item) + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +# ==================== insert Object ==================== +proc insert*[T](self:SqliteQuery, items:T) {.async.} = + let sql = self.insertValueBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*[T](self:SqliteQuery, items:seq[T]) {.async.} = + let items = items.mapIt(%it) + let sql = self.insertValuesBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*[T](self: SqliteQuery, items: T, key="id"):Future[string] {.async.} = + let sql = self.insertValueBuilder(%items) + self.log.logger(sql) + return self.insertId(sql, key).await + + +proc insertId*[T](self: SqliteQuery, items: seq[T], key="id"):Future[seq[string]] {.async.} = + result = newSeq[string](items.len) + for i, item in items: + let sql = self.insertValueBuilder(%item) + self.log.logger(sql) + result[i] = self.insertId(sql, key).await + self.placeHolder = newJArray() + + +# ==================== update ==================== +proc update*(self:SqliteQuery, items:JsonNode) {.async.} = + let sql = self.updateBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc update*[T](self:SqliteQuery, items:T) {.async.} = + let sql = self.updateBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self:SqliteQuery) {.async.} = + let sql = self.deleteBuilder() + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self:SqliteQuery, id:int, key="id") {.async.} = + let sql = self.deleteByIdBuilder(id, key) + self.log.logger(sql) + self.placeHolder.add(%*{"key":key, "value":id}) + self.exec(sql).await + + +proc columns*(self:SqliteQuery):Future[seq[string]] {.async.} = + let sql = self.columnBuilder() + self.log.logger(sql) + return self.getColumns(sql, self.placeHolder).await + + +proc count*(self:SqliteQuery):Future[int] {.async.} = + let sql = self.countBuilder() + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + return response.get["aggregate"].getStr().parseInt() + else: + return 0 + + +proc min*(self:SqliteQuery, column:string):Future[Option[string]] {.async.} = + let sql = self.minBuilder(column) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc max*(self:SqliteQuery, column:string):Future[Option[string]] {.async.} = + let sql = self.maxBuilder(column) + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + case response.get["aggregate"].kind + of JInt: + return some($(response.get["aggregate"].getInt)) + of JFloat: + return some($(response.get["aggregate"].getFloat)) + else: + return some(response.get["aggregate"].getStr) + else: + return none(string) + + +proc avg*(self:SqliteQuery, column:string):Future[Option[float]]{.async.} = + let sql = self.avgBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getStr().parseFloat.some + else: + return none(float) + + +proc sum*(self:SqliteQuery, column:string):Future[Option[float]]{.async.} = + let sql = self.sumBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["aggregate"].getStr.parseFloat.some + else: + return none(float) + + +proc begin*(self:SqliteConnections) {.async.} = + self.log.logger("BEGIN") + self.transactionStart().await + + +proc rollback*(self:SqliteConnections) {.async.} = + self.log.logger("ROLLBACK") + self.transactionEnd("ROLLBACK").await + + +proc commit*(self:SqliteConnections) {.async.} = + self.log.logger("COMMIT") + self.transactionEnd("COMMIT").await + + +proc get*(self: RawSqliteQuery):Future[seq[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRows(self.queryString).await + + +proc getPlain*(self: RawSqliteQuery):Future[seq[seq[string]]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRowsPlain(self.queryString, self.placeHolder).await + + +proc exec*(self: RawSqliteQuery) {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + self.exec(self.queryString, self.placeHolder).await + + +proc first*(self: RawSqliteQuery):Future[Option[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRow(self.queryString).await + + +proc firstPlain*(self: RawSqliteQuery):Future[seq[string]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRowPlain(self.queryString, self.placeHolder).await + + +proc close*(self: SqlitePreparedStatement) {.async.} = + if self.isNil or self.isClosed: + return + self.isClosed = true + if not self.entry.isNil: + if self.entry.refCount > 0: + self.entry.refCount -= 1 + touchStmtEntry(self.entry) + + +proc flushStmt*(self: SqliteConnections, sql: string) {.async.} = + if not hasPreparedEntry(self.pools.preparedCache, sql): + return + let entry = self.pools.preparedCache[sql] + for i, stmt in entry.stmts: + if stmt.isNil: + continue + try: + discard finalize(stmt) + except CatchableError: + self.log.echoErrorMsg("finalize failed for prepared stmt: " & getCurrentExceptionMsg()) + entry.stmts[i] = nil + self.pools.preparedCache.del(sql) + + +proc clearStmtCache*(self: SqliteConnections) {.async.} = + let keys = toSeq(self.pools.preparedCache.keys) + for sql in keys: + await self.flushStmt(sql) + + +# ================================================================================ +# public prepared exec +# ================================================================================ + +proc get*(self: SqlitePreparedStatement, args: seq[string]): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(args.toPreparedParams) + + +proc get*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: seq[string]): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(ctx, args.toPreparedParams) + + +proc get*(self: SqlitePreparedStatement, args: JsonNode): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(args.toPreparedParams) + + +proc get*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: JsonNode): Future[seq[JsonNode]] {.async.} = + return await self.preparedGet(ctx, args.toPreparedParams) + + +proc first*(self: SqlitePreparedStatement, args: seq[string]): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(args.toPreparedParams) + + +proc first*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: seq[string]): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(ctx, args.toPreparedParams) + + +proc first*(self: SqlitePreparedStatement, args: JsonNode): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(args.toPreparedParams) + + +proc first*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: JsonNode): Future[Option[JsonNode]] {.async.} = + return await self.preparedFirst(ctx, args.toPreparedParams) + + +proc getPlain*(self: SqlitePreparedStatement, args: seq[string]): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(args.toPreparedParams) + + +proc getPlain*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: seq[string]): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(ctx, args.toPreparedParams) + + +proc getPlain*(self: SqlitePreparedStatement, args: JsonNode): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(args.toPreparedParams) + + +proc getPlain*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: JsonNode): Future[seq[seq[string]]] {.async.} = + return await self.preparedGetPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: SqlitePreparedStatement, args: seq[string]): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(args.toPreparedParams) + + +proc firstPlain*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: seq[string]): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(ctx, args.toPreparedParams) + + +proc firstPlain*(self: SqlitePreparedStatement, args: JsonNode): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(args.toPreparedParams) + + +proc firstPlain*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: JsonNode): Future[seq[string]] {.async.} = + return await self.preparedFirstPlain(ctx, args.toPreparedParams) + + +proc exec*(self: SqlitePreparedStatement, args: seq[string]) {.async.} = + await self.preparedExec(args.toPreparedParams) + + +proc exec*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: seq[string]) {.async.} = + await self.preparedExec(ctx, args.toPreparedParams) + + +proc exec*(self: SqlitePreparedStatement, args: JsonNode) {.async.} = + await self.preparedExec(args.toPreparedParams) + + +proc exec*(self: SqlitePreparedStatement, ctx: SqlitePreparedContext, args: JsonNode) {.async.} = + await self.preparedExec(ctx, args.toPreparedParams) + + + +template seeder*(rdb:SqliteConnections, tableName:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table is empty. + block: + if rdb.table(tableName).count().waitFor == 0: + body + + +template seeder*(rdb:SqliteConnections, tableName, column:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table or specified column is empty. + block: + if rdb.select(column).table(tableName).count().waitFor == 0: + body diff --git a/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_open.nim b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_open.nim new file mode 100644 index 0000000..b44399b --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_open.nim @@ -0,0 +1,63 @@ +import std/asyncdispatch +import std/deques +import std/strutils +import std/tables +import std/times +import ../../libs/database_url +import ../../libs/sqlite/sqlite_rdb +import ../../log +import ./sqlite_types + + +proc openSqlite(database: string; maxConnections: int; timeout: int; + shouldDisplayLog: bool; shouldOutputLogFile: bool; logDir: string; + maxConnectionLifetime: int = DEFAULT_CONN_MAX_LIFETIME_SECONDS; + maxConnectionIdleTime: int = DEFAULT_CONN_MAX_IDLE_SECONDS): SqliteConnections = + var conns = newSeq[Connection](maxConnections) + for i in 0.. 0 and database.contains("://"): + let parsed = parseDatabaseUrl(asDatabaseUrl(database)) + requireDatabaseUrlScheme(parsed, ["sqlite"], "SQLite") + return openSqlite(sqliteDatabasePath(parsed), maxConnections, timeout, shouldDisplayLog, shouldOutputLogFile, logDir, maxConnectionLifetime, maxConnectionIdleTime) + + return openSqlite(database, maxConnections, timeout, shouldDisplayLog, shouldOutputLogFile, logDir, maxConnectionLifetime, maxConnectionIdleTime) + + +proc dbOpen*(_:type SQLite3, databaseUrl: DatabaseUrl, + maxConnections: int = 1, timeout=30, + shouldDisplayLog=false, shouldOutputLogFile=false, logDir="", + maxConnectionLifetime=DEFAULT_CONN_MAX_LIFETIME_SECONDS, + maxConnectionIdleTime=DEFAULT_CONN_MAX_IDLE_SECONDS): SqliteConnections = + let parsed = parseDatabaseUrl(databaseUrl) + requireDatabaseUrlScheme(parsed, ["sqlite"], "SQLite") + let database = sqliteDatabasePath(parsed) + return openSqlite(database, maxConnections, timeout, shouldDisplayLog, shouldOutputLogFile, logDir, maxConnectionLifetime, maxConnectionIdleTime) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_query.nim b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_query.nim new file mode 100644 index 0000000..4563955 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_query.nim @@ -0,0 +1,411 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strutils +import std/times +import ../../libs/sqlite/sqlite_impl +import ../../enums +import ./sqlite_types + + +proc select*(self:SqliteConnections, columnsArg:varargs[string]):SqliteQuery = + let query = newJObject() + + if columnsArg.len == 0: + query["select"] = %["*"] + else: + query["select"] = %columnsArg + + let sqliteQuery = SqliteQuery( + log: self.log, + pools: self.pools, + query: query, + queryString: "", + placeHolder: newJArray(), + isInTransaction: self.isInTransaction, + transactionConn: self.transactionConn, + ) + return sqliteQuery + + +proc table*(self:SqliteConnections, tableArg: string): SqliteQuery = + let query = newJObject() + query["table"] = %tableArg + + let sqliteQuery = SqliteQuery( + log: self.log, + pools: self.pools, + query: query, + queryString: "", + placeHolder: newJArray(), + isInTransaction: self.isInTransaction, + transactionConn: self.transactionConn, + ) + return sqliteQuery + + +proc table*(self:SqliteQuery, tableArg: string): SqliteQuery = + self.query["table"] = %tableArg + return self + + +proc `distinct`*(self: SqliteQuery): SqliteQuery = + self.query["distinct"] = %true + return self + +# ============================== Conditions ============================== + +proc join*(self: SqliteQuery, table: string, column1: string, symbol: string, + column2: string): SqliteQuery = + if self.query.hasKey("join") == false: + self.query["join"] = %*[{ + "table": table, + "column1": column1, + "symbol": symbol, + "column2": column2 + }] + else: + self.query["join"].add(%*{ + "table": table, + "column1": column1, + "symbol": symbol, + "column2": column2 + }) + return self + + +proc leftJoin*(self: SqliteQuery, table: string, column1: string, symbol: string, + column2: string): SqliteQuery = + if self.query.hasKey("left_join") == false: + self.query["left_join"] = %*[{ + "table": table, + "column1": column1, + "symbol": symbol, + "column2": column2 + }] + else: + self.query["left_join"].add(%*{ + "table": table, + "column1": column1, + "symbol": symbol, + "column2": column2 + }) + return self + + +const whereSymbols = ["is", "is not", "=", "!=", "<", "<=", ">=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"] +const whereSymbolsError = """Arg position 3 is only allowed of ["is", "is not", "=", "!=", "<", "<=", ">=", ">", "<>", "LIKE","%LIKE","LIKE%","%LIKE%"]""" + +proc where*(self: SqliteQuery, column: string, symbol: string, + value: string|int|float): SqliteQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key": column, "value": value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: SqliteQuery, column: string, symbol: string, + value: bool): SqliteQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + + +proc where*(self: SqliteQuery, column: string, symbol: string, value: nil.type): SqliteQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orWhere*(self: SqliteQuery, column: string, symbol: string, + value: string|int|float|bool): SqliteQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["or_where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + +proc orWhere*(self: SqliteQuery, column: string, symbol: string, value: nil.type): SqliteQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["or_where"].add(%*{ + "column": column, + "symbol": symbol, + "value": "null" + }) + return self + +proc whereBetween*(self:SqliteQuery, column:string, width:array[2, int|float]): SqliteQuery = + if self.query.hasKey("where_between") == false: + self.query["where_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between"].add(%*{ + "column": column, + "width": width + }) + return self + +proc whereBetween*(self:SqliteQuery, column:string, width:array[2, string]): SqliteQuery = + if self.query.hasKey("where_between_string") == false: + self.query["where_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + +proc whereNotBetween*(self:SqliteQuery, column:string, width:array[2, int|float]): SqliteQuery = + if self.query.hasKey("where_not_between") == false: + self.query["where_not_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between"].add(%*{ + "column": column, + "width": width + }) + return self + +proc whereNotBetween*(self:SqliteQuery, column:string, width:array[2, string]): SqliteQuery = + if self.query.hasKey("where_not_between_string") == false: + self.query["where_not_between_string"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between_string"].add(%*{ + "column": column, + "width": width + }) + return self + +proc whereIn*(self:SqliteQuery, column:string, width:seq[int|float|string]): SqliteQuery = + if self.query.hasKey("where_in") == false: + self.query["where_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotIn*(self:SqliteQuery, column:string, width:seq[int|float|string]): SqliteQuery = + if self.query.hasKey("where_not_in") == false: + self.query["where_not_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNull*(self:SqliteQuery, column:string): SqliteQuery = + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column + }] + else: + self.query["where_null"].add(%*{ + "column": column + }) + return self + + +proc groupBy*(self:SqliteQuery, column:string): SqliteQuery = + if self.query.hasKey("group_by") == false: + self.query["group_by"] = %*[{"column": column}] + else: + self.query["group_by"].add(%*{"column": column}) + return self + + +proc having*(self: SqliteQuery, column: string, symbol: string, + value: string|int|float|bool): SqliteQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + self.placeHolder.add(%*{"key":column, "value":value}) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "?" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "?" + } + ) + return self + +proc having*(self: SqliteQuery, column: string, symbol: string, value: nil.type): SqliteQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": "null" + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": "null" + } + ) + return self + + +proc orderBy*(self:SqliteQuery, column:string, order:Order): SqliteQuery = + if self.query.hasKey("order_by") == false: + self.query["order_by"] = %*[{ + "column": column, + "order": $order + }] + else: + self.query["order_by"].add(%*{ + "column": column, + "order": $order + }) + return self + + +proc limit*(self: SqliteQuery, num: int): SqliteQuery = + self.query["limit"] = %num + return self + + +proc offset*(self: SqliteQuery, num: int): SqliteQuery = + self.query["offset"] = %num + return self + + +proc raw*(self:SqliteConnections, sql:string, arges=newJArray()): RawSqliteQuery = + ## arges is `JArray` + ## + ## can't use BLOB data. + let rawQueryRdb = RawSqliteQuery( + log: self.log, + pools: self.pools, + query: newJObject(), + queryString: sql, + placeHolder: arges, + isInTransaction: false, + transactionConn: 0 + ) + return rawQueryRdb diff --git a/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_transaction.nim b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_transaction.nim new file mode 100644 index 0000000..778f30a --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_transaction.nim @@ -0,0 +1,42 @@ +import std/macros +import std/strutils +import std/strformat +import ../../models/sqlite/sqlite_types + + +when NimMajor == 1: + macro rdbTransaction(rdb:SqliteConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body +else: + macro rdbTransaction(rdb:SqliteConnections, callback: untyped):untyped = + var callbackStr = callback.repr + callbackStr.removePrefix + callbackStr = callbackStr.indent(4) + callbackStr = fmt""" +block: + {rdb.repr}.begin().await + try: +{callbackStr} + {rdb.repr}.commit().await + except DbError, CatchableError: + {rdb.repr}.rollback().await +""" + let body = callbackStr.parseStmt() + return body + + +template transaction*(rdb:SqliteConnections, callback: untyped) = + rdbTransaction(rdb, callback) diff --git a/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_types.nim b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_types.nim new file mode 100644 index 0000000..5ffab6f --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/sqlite/sqlite_types.nim @@ -0,0 +1,93 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/tables +import ../database_types +import ../../log +import ../../libs/sqlite/sqlite_rdb + + +type SQLite3* = object + + +const DEFAULT_CONN_MAX_LIFETIME_SECONDS* = 300 +const DEFAULT_CONN_MAX_IDLE_SECONDS* = 300 + + +type Connection* = ref object + conn*: PSqlite3 + isBusy*: bool + createdAt*: int64 + lastUsedAt*: int64 + + +type SqlitePreparedEntry* = ref object + sql*: string + nArgs*: int + stmts*: seq[PStmt] + refCount*: int + lastUsedAt*: int64 + + +type Connections* = ref object + conns*: seq[Connection] + timeout*: int + maxConnectionLifetime*: int + maxConnectionIdleTime*: int + database*: string + ## `getFreeConn` が接続を待つときに積む Future。`returnConn` が先頭から 1 件だけ完了させる。 + waiters*: Deque[Future[void]] + ## `exec` / `insertId` 用。テーブルごとに PRAGMA table_info の結果を初回のみ保持する。 + columnTypeCache*: Table[string, seq[(string, string)]] + preparedCache*: Table[string, SqlitePreparedEntry] + + +## created by `let rdb = dbOpen(SQLite3, "/path/to/sqlite.db")` +type SqliteConnections* = ref object + log*: LogSetting + pools*:Connections + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type SqlitePreparedContext* = ref object + owner*: SqliteConnections + connI*: int + + +## created by `rdb.select("columnName")` or `rdb.table("tableName")` +type SqliteQuery* = ref object + log*: LogSetting + pools*:Connections + query*: JsonNode # JObject + queryString*: string + placeHolder*: JsonNode # JArray [{"key":"user", "value":"user1"}] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type RawSqliteQuery* = ref object + log*: LogSetting + pools*:Connections + query*: JsonNode # JObject + queryString*: string + placeHolder*: JsonNode # JArray ["user1", "user1@example.com"] + # for transaction + isInTransaction*: bool + transactionConn*: int + + +type SqlitePreparedStatement* = ref object + owner*: SqliteConnections + entry*: SqlitePreparedEntry + sql*: string + isClosed*: bool + cachedColumns*: DbColumns + hasCachedColumns*: bool + + + +proc `$`*(self:SqliteConnections|SqliteQuery|RawSqliteQuery):string = + return "SQLite" diff --git a/clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_builder.nim b/clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_builder.nim new file mode 100644 index 0000000..bdc6e1e --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_builder.nim @@ -0,0 +1,197 @@ +import std/json +import ../surreal_types +import ./surreal_generator + + +# ==================== SELECT ==================== + +proc selectBuilder*(self: SurrealQuery): string = + return self + .selectSql() + .fromSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + # .whereBetweenStringSql() + .whereNotBetweenSql() + # .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + #.havingSql() + .orderBySql() + .limitSql() + .startSql() + .fetchSql() + .parallelSql() + .queryString + + +proc selectFirstBuilder*(self: SurrealQuery): string = + return self + .selectSql() + .fromSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + # .whereBetweenStringSql() + .whereNotBetweenSql() + # .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + #.havingSql() + .orderBySql() + .selectFirstSql() # LIMIT + .startSql() + .fetchSql() + .parallelSql() + .queryString + + +proc selectFindBuilder*(self: SurrealQuery, id:SurrealId, key: string): string = + return self + .selectSql() + .fromSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + # .whereBetweenStringSql() + .whereNotBetweenSql() + # .whereNotBetweenStringSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .selectByIdSql(id, key) # LIMIT + .fetchSql() + .parallelSql() + .queryString + + +# ==================== INSERT ==================== + +proc insertValueBuilder*(self: SurrealQuery, items: JsonNode): string = + return self + .insertSql() + .insertValueSql(items) + .parallelSql() + .queryString + +proc insertValuesBuilder*(self: SurrealQuery, items: openArray[JsonNode]): string = + return self + .insertSql() + .insertValuesSql(items) + .parallelSql() + .queryString + + +# ==================== UPDATE ==================== + +proc updateBuilder*(self:SurrealQuery, items:JsonNode):string = + return self + .updateSql() + .updateValuesSql(items) + .whereSql() + .orWhereSql() + .limitSql() + .startSql() + .parallelSql() + .queryString + + +proc updateMergeBuilder*(self:SurrealQuery, id:string, items:JsonNode):string = + return self + .updateMergeSql(id, items) + # .whereSql() + # .orWhereSql() + # .limitSql() + # .startSql() + .parallelSql() + .queryString + + +# ==================== DELETE ==================== + +proc deleteBuilder*(self: SurrealQuery): string = + return self + .deleteSql() + .fromSql() + .whereSql() + .orWhereSql() + .limitSql() + .startSql() + .queryString + +proc deleteByIdBuilder*(self: SurrealQuery, id: string): string = + return self + .deleteSql() + .deleteByIdSql(id) + .queryString + + +# ==================== Aggregates ==================== + +proc countBuilder*(self:SurrealQuery): string = + let sql = self + .selectCountSql() + .fromSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + #.havingSql() + .orderBySql() + .limitSql() + .startSql() + .fetchSql() + .parallelSql() + .queryString + return sql & " GROUP ALL" + +proc selectAvgBuilder*(self:SurrealQuery, column:string): string = + return self + .selectAvgSql(column) + .fromSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + #.havingSql() + .orderBySql() + .limitSql() + .startSql() + .fetchSql() + .parallelSql() + .groupAllSql() + .queryString + +proc selectSumBuilder*(self:SurrealQuery, column:string): string = + return self + .selectSumSql(column) + .fromSql() + .whereSql() + .orWhereSql() + .whereBetweenSql() + .whereNotBetweenSql() + .whereInSql() + .whereNotInSql() + .whereNullSql() + .groupBySql() + #.havingSql() + .orderBySql() + .limitSql() + .startSql() + .fetchSql() + .parallelSql() + .groupAllSql() + .queryString diff --git a/clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_generator.nim b/clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_generator.nim new file mode 100644 index 0000000..9bfc8d6 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/surreal/query/surreal_generator.nim @@ -0,0 +1,391 @@ +import std/json +import std/strformat +import std/strutils +# import ../databases/surreal_lib +import ../surreal_types + + +# proc quoteTable*(input:var string) = +# input = &"`{input}`" + +proc quote*(input:string):string = + ## "User.id as userId" => `User`.`id` as `userId` + ## + ## "count(id) as count" => count(`id`) as `count` + ## + ## "math::max(id) as maxId" => math::max(`id`) as `maxId` + ## + var tmp = newSeq[string]() + for segment in input.split("."): + if segment.contains(" as "): + # Split once on ' as ' to separate the expression and its alias + let parts = segment.split(" as ", maxsplit = 1) + let expression = parts[0] + let alias = parts[1] + if expression.contains("("): + # Extract function name and column name using index-based slicing + let funcStart = expression.find('(') + let funcEnd = expression.find(')', funcStart) + let funcName = expression[0 ..< funcStart] + let columnName = expression[funcStart + 1 ..< funcEnd] + + if columnName.len > 0: + tmp.add(&"{funcName}(`{columnName}`) as `{alias}`") + else: + tmp.add(&"{funcName}() as `{alias}`") + else: + # Quote the expression and alias + tmp.add(&"`{expression}` as `{alias}`") + elif segment.contains("("): + # Leave functions without alias as is + tmp.add(segment) + else: + # Quote standalone identifiers + tmp.add(&"`{segment}`") + return tmp.join(".") + + +# ==================== SELECT ==================== +proc selectSql*(self: SurrealQuery): SurrealQuery = + var queryString = "SELECT" + + if self.query.hasKey("select"): + for i, item in self.query["select"].getElems(): + if i > 0: queryString.add(",") + var column = item.getStr() + column = quote(column) + queryString.add(&" {column}") + else: + queryString.add(" *") + + self.queryString = queryString + return self + + +proc fromSql*(self: SurrealQuery): SurrealQuery = + var table = self.query["table"].getStr() + table = quote(table) + self.queryString.add(&" FROM {table}") + return self + + +proc maxFromSql*(self:SurrealQuery, column:string):SurrealQuery = + var table = self.query["table"].getStr() + table = quote(table) + self.queryString.add(&" FROM array::max({column})") + return self + + +proc selectFirstSql*(self:SurrealQuery): SurrealQuery = + self.queryString.add(" LIMIT 1") + return self + + +proc selectByIdSql*(self:SurrealQuery, id:SurrealId, key:string): SurrealQuery = + var key = key + key = quote(key) + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND {key} = ? LIMIT 1") + self.placeHolder.add(%id.rawId) + else: + self.queryString.add(&" WHERE {key} = ? LIMIT 1") + self.placeHolder.add(%id.rawId) + return self + + +proc whereSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("where"): + for i, row in self.query["where"].getElems(): + var column = row["column"].getStr() + column = quote(column) + var symbol = row["symbol"].getStr() + var value = row["value"] + + if i == 0: + self.queryString.add(&" WHERE {column} {symbol} ?") + self.placeHolder.add(value) + else: + self.queryString.add(&" AND {column} {symbol} ?") + self.placeHolder.add(value) + return self + + +proc orWhereSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("or_where"): + for row in self.query["or_where"]: + var column = row["column"].getStr() + column = quote(column) + var symbol = row["symbol"].getStr() + var value = row["value"] + + if self.queryString.contains("WHERE"): + self.queryString.add(&" OR {column} {symbol} ?") + self.placeHolder.add(value) + else: + self.queryString.add(&" WHERE {column} {symbol} ?") + self.placeHolder.add(value) + return self + + +proc whereBetweenSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("where_between"): + for row in self.query["where_between"]: + var column = row["column"].getStr() + column = quote(column) + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND ? <= {column} AND {column} <= ?") + else: + self.queryString.add(&" WHERE ? <= {column} AND {column} <= ?") + + self.placeHolder.add(row["width"][0]) + self.placeHolder.add(row["width"][1]) + + return self + + +proc whereNotBetweenSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("where_not_between"): + for row in self.query["where_not_between"]: + var column = row["column"].getStr() + column = quote(column) + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND ? > {column} AND {column} < ?") + else: + self.queryString.add(&" WHERE ? > {column} AND {column} < ?") + + self.placeHolder.add(row["width"][0]) + self.placeHolder.add(row["width"][1]) + + return self + + +proc whereInSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("where_in"): + for row in self.query["where_in"]: + var column = row["column"].getStr() + column = quote(column) + + var values = "" + var i = 0 + for row in row["width"].items: + defer: i.inc() + if i > 0: values.add(", ") + values.add("?") + self.placeHolder.add(row) + values.add("") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND [{values}] CONTAINS {column}") + else: + self.queryString.add(&" WHERE [{values}] CONTAINS {column}") + + return self + + +proc whereNotInSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("where_not_in"): + for row in self.query["where_not_in"]: + var column = row["column"].getStr() + column = quote(column) + + var values = "" + var i = 0 + for row in row["width"].items: + defer: i.inc() + if i > 0: values.add(", ") + values.add("?") + self.placeHolder.add(row) + values.add("") + + if self.queryString.contains("WHERE"): + self.queryString.add(&" AND [{values}] CONTAINSNOT {column}") + else: + self.queryString.add(&" WHERE [{values}] CONTAINSNOT {column}") + + return self + + +proc whereNullSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("where_null"): + for row in self.query["where_null"]: + var column = row["column"].getStr() + column = quote(column) + + if self.queryString.contains("WHERE"): + self.queryString.add(&" OR {column} IS NONE") + else: + self.queryString.add(&" WHERE {column} IS NONE") + + return self + + +proc groupBySql*(self:SurrealQuery): SurrealQuery = + if self.query.hasKey("group_by"): + for row in self.query["group_by"]: + var column = row["column"].getStr() + column = quote(column) + if self.queryString.contains("GROUP BY"): + self.queryString.add(&", {column}") + else: + self.queryString.add(&" GROUP BY {column}") + return self + + +proc orderBySql*(self:SurrealQuery): SurrealQuery = + if self.query.hasKey("order_by"): + for row in self.query["order_by"]: + var column = row["column"].getStr() + column = quote(column) + let collation = row["collation"].getStr() + let order = row["order"].getStr() + + if self.queryString.contains("ORDER BY"): + self.queryString.add(&", {column} {collation} {order}") + else: + self.queryString.add(&" ORDER BY {column} {collation} {order}") + return self + + +proc limitSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("limit"): + var num = self.query["limit"].getInt() + self.queryString.add(&" LIMIT {num}") + return self + + +proc startSql*(self: SurrealQuery): SurrealQuery = + if self.query.hasKey("start"): + var num = self.query["start"].getInt() + self.queryString.add(&" START {num}") + return self + + +proc fetchSql*(self:SurrealQuery):SurrealQuery = + if self.query.hasKey("fetch"): + self.queryString.add(" FETCH") + for i, item in self.query["fetch"].getElems(): + if i > 0: self.queryString.add(",") + var column = item.getStr() + column = quote(column) + self.queryString.add(&" {column}") + return self + + +proc parallelSql*(self:SurrealQuery):SurrealQuery = + if self.query.hasKey("parallel") and self.query["parallel"].getBool(): + self.queryString.add(" PARALLEL") + return self + + +# ==================== INSERT ==================== + +proc insertSql*(self: SurrealQuery): SurrealQuery = + var table = self.query["table"].getStr() + table = quote(table) + self.queryString = &"INSERT INTO {table}" + return self + + +proc insertValueSql*(self: SurrealQuery, items: JsonNode): SurrealQuery = + self.queryString.add(" {") + var i = 0 + for (key, item) in items.pairs: + defer: i.inc() + if i > 0: self.queryString.add(", ") + self.queryString.add(&"{key}: ?") + self.placeHolder.add(item) + self.queryString.add("}") + return self + + +proc insertValuesSql*(self: SurrealQuery, rows: openArray[JsonNode]): SurrealQuery = + self.queryString.add(" [") + for i, row in rows: + if i > 0: self.queryString.add(", ") + var j = 0 + self.queryString.add("{") + for (key, item) in row.pairs: + defer: j.inc() + if j > 0: self.queryString.add(", ") + self.queryString.add(&"{key}: ?") + self.placeHolder.add(item) + self.queryString.add("}") + self.queryString.add("]") + return self + + +# ==================== UPDATE ==================== + +proc updateSql*(self: SurrealQuery): SurrealQuery = + var queryString = "" + queryString.add("UPDATE") + + var table = self.query["table"].getStr() + table = quote(table) + queryString.add(&" {table} SET ") + self.queryString = queryString + return self + + +proc updateValuesSql*(self: SurrealQuery, items:JsonNode): SurrealQuery = + var i = 0 + for key, val in items.pairs: + defer: i.inc() + if i > 0: self.queryString.add(", ") + self.queryString.add(&"{key} = ?") + self.placeHolder.add(val) + + return self + + +proc updateMergeSql*(self: SurrealQuery, id:string, items:JsonNode):SurrealQuery = + self.queryString = &"UPDATE {id} MERGE {items}" + return self + + +# ==================== DELETE ==================== + +proc deleteSql*(self: SurrealQuery): SurrealQuery = + self.queryString = "DELETE" + return self + + +proc deleteByIdSql*(self: SurrealQuery, id: string): SurrealQuery = + self.queryString.add(&" ?") + self.placeHolder.add(%id) + return self + + +# ==================== Aggregates ==================== + +proc selectCountSql*(self: SurrealQuery): SurrealQuery = + var queryString = + if self.query.hasKey("select"): + var column = self.query["select"][0].getStr + column = quote(column) + &"{column}" + else: + "" + self.queryString = &"SELECT count({queryString}) AS total" + return self + + +proc selectAvgSql*(self: SurrealQuery, column:string): SurrealQuery = + var column = column + column = quote(column) + self.queryString = &"SELECT math::mean({column}) AS avg" + return self + + +proc selectSumSql*(self: SurrealQuery, column:string): SurrealQuery = + var column = column + column = quote(column) + self.queryString = &"SELECT math::sum({column}) AS sum" + return self + + +proc groupAllSql*(self: SurrealQuery):SurrealQuery = + self.queryString.add(" GROUP ALL") + return self diff --git a/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_exec.nim b/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_exec.nim new file mode 100644 index 0000000..18db008 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_exec.nim @@ -0,0 +1,982 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/monotimes +import std/options +import std/sequtils +import std/strformat +import std/strutils +import std/tables +import std/times +import ../../error +import ../../libs/surreal/surreal_lib +import ../../libs/surreal/surreal_impl +import ../../log +import ../../enums +import ../database_types +import ../../prepared_param +import ./query/surreal_builder +import ./surreal_types +import ./surreal_query + + +# ================================================================================ +# connection +# ================================================================================ + +proc removePoolWaiter(pools: Connections, w: Future[void]) = + var kept = initDeque[Future[void]]() + while pools.waiters.len > 0: + let x = pools.waiters.popFirst() + if x != w: + kept.addLast(x) + pools.waiters = move(kept) + +proc wakeOnePoolWaiter(pools: Connections) = + while pools.waiters.len > 0: + let w = pools.waiters.popFirst() + if w.finished: + continue + w.complete() + break + +proc surrealPoolRemainingMs(deadline: MonoTime): int = + let left = (deadline - getMonoTime()).inMilliseconds + if left <= 0: + return 0 + if left > int64(high(int)): + return high(int) + result = int(left) + if result < 1: + result = 1 + +proc getFreeConn(self: SurrealConnections | SurrealQuery | RawSurrealQuery): Future[int] {.async.} = + let deadline = getMonoTime() + initDuration(seconds = self.pools.timeout) + while true: + for i in 0 ..< self.pools.conns.len: + if not self.pools.conns[i].isBusy: + self.pools.conns[i].isBusy = true + when defined(check_pool): + echo "=== getFreeConn ", i + return i + if getMonoTime() >= deadline: + return errorConnectionNum + let w = newFuture[void]("getFreeConn.poolWait") + self.pools.waiters.addLast(w) + var ms = surrealPoolRemainingMs(deadline) + if ms < 1: + ms = 1 + let ok = await withTimeout(w, ms) + if not ok: + removePoolWaiter(self.pools, w) + return errorConnectionNum + + +proc returnConn(self: SurrealConnections | SurrealQuery | RawSurrealQuery, i: int) {.async.} = + if i != errorConnectionNum: + self.pools.conns[i].isBusy = false + wakeOnePoolWaiter(self.pools) + + +proc raisePoolTimeout(self: SurrealConnections | SurrealQuery | RawSurrealQuery | SurrealPreparedStatement) {.noreturn.} = + raise newException(DbError, "Timed out while waiting for a free SurrealDB connection") + + +proc touchStmtEntry(entry: SurrealPreparedEntry) = + entry.lastUsedAt = getTime().toUnix() + + +proc mustBeOpen(self: SurrealPreparedStatement) = + if self.isNil or self.owner.isNil or self.isClosed: + raise newException(DbError, "SurrealDB prepared statement is already closed") + + +proc hasPreparedEntry(cache: Table[string, SurrealPreparedEntry], sql: string): bool = + for key in cache.keys: + if key == sql: + return true + return false + + +proc getStmtEntry(self: SurrealConnections, sql: string): SurrealPreparedEntry = + if hasPreparedEntry(self.pools.preparedCache, sql): + return self.pools.preparedCache[sql] + let entry = SurrealPreparedEntry( + sql: sql, + normalizedSql: sql.questionToDaller(), + nArgs: countQuestionMarks(sql), + refCount: 0, + lastUsedAt: getTime().toUnix(), + ) + self.pools.preparedCache[sql] = entry + return entry + + +proc prepare*(self: SurrealConnections, sql: string): SurrealPreparedStatement = + let entry = self.getStmtEntry(sql) + entry.refCount += 1 + touchStmtEntry(entry) + new(result) + result.owner = self + result.entry = entry + result.sql = sql + result.nArgs = entry.nArgs + result.isClosed = false + + +proc withConn*( + self: SurrealConnections, + body: proc (ctx: SurrealPreparedContext): Future[void] +) {.async.} = + let connI = getFreeConn(self).await + if connI == errorConnectionNum: + raisePoolTimeout(self) + defer: + self.returnConn(connI).await + + let ctx = SurrealPreparedContext(owner: self, connI: connI) + await body(ctx) + + +proc verifyCtx(self: SurrealPreparedStatement, ctx: SurrealPreparedContext) = + self.mustBeOpen() + if ctx.isNil: + raise newException(DbError, "SurrealDB prepared context is nil") + if ctx.owner.isNil or ctx.owner != self.owner: + raise newException(DbError, "SurrealDB prepared context owner mismatch") + if ctx.connI < 0 or ctx.connI >= self.owner.pools.conns.len: + raise newException(DbError, "SurrealDB prepared context has invalid connection index") + + +proc toPreparedArgsJson(args: seq[string]): JsonNode = + result = newJArray() + for arg in args: + if arg == "NULL" or arg == "null": + result.add(newJNull()) + else: + result.add(%arg) + + +proc buildPreparedSql(self: SurrealPreparedStatement, args: JsonNode): string = + self.mustBeOpen() + touchStmtEntry(self.entry) + result = dbFormatPrepared(self.entry.normalizedSql, args) + + +proc getPreparedRowsOnConn( + self: SurrealPreparedStatement, + connI: int, + args: JsonNode +): Future[seq[JsonNode]] {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "SurrealDB prepared statement received an invalid connection index") + + let sql = self.buildPreparedSql(args) + let rows = surreal_impl.query( + self.owner.pools.conns[connI].conn, + sql, + newJArray(), + self.owner.pools.timeout + ).await + + if rows.kind != JArray or rows.len == 0: + return newSeq[JsonNode](0) + return rows.getElems() + + +proc getPreparedRowOnConn( + self: SurrealPreparedStatement, + connI: int, + args: JsonNode +): Future[Option[JsonNode]] {.async.} = + let rows = await self.getPreparedRowsOnConn(connI, args) + if rows.len == 0: + return none(JsonNode) + return rows[0].some + + +proc execPreparedOnConn( + self: SurrealPreparedStatement, + connI: int, + args: JsonNode +) {.async.} = + self.mustBeOpen() + if connI < 0 or connI >= self.owner.pools.conns.len: + raise newException(DbError, "SurrealDB prepared statement received an invalid connection index") + + let sql = self.buildPreparedSql(args) + await surreal_impl.exec( + self.owner.pools.conns[connI].conn, + sql, + newJArray(), + self.owner.pools.timeout + ) + + +proc getPreparedRows(self: SurrealPreparedStatement, args: JsonNode): Future[seq[JsonNode]] {.async.} = + let connI = await getFreeConn(self.owner) + if connI == errorConnectionNum: + raisePoolTimeout(self) + defer: + await self.owner.returnConn(connI) + return await self.getPreparedRowsOnConn(connI, args) + + +proc getPreparedRows(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: JsonNode): Future[seq[JsonNode]] {.async.} = + self.verifyCtx(ctx) + return await self.getPreparedRowsOnConn(ctx.connI, args) + + +proc getPreparedRow(self: SurrealPreparedStatement, args: JsonNode): Future[Option[JsonNode]] {.async.} = + let rows = await self.getPreparedRows(args) + if rows.len == 0: + return none(JsonNode) + return rows[0].some + + +proc getPreparedRow(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: JsonNode): Future[Option[JsonNode]] {.async.} = + self.verifyCtx(ctx) + return await self.getPreparedRowOnConn(ctx.connI, args) + + +proc execPrepared(self: SurrealPreparedStatement, args: JsonNode) {.async.} = + let connI = await getFreeConn(self.owner) + if connI == errorConnectionNum: + raisePoolTimeout(self) + defer: + await self.owner.returnConn(connI) + await self.execPreparedOnConn(connI, args) + + +proc execPrepared(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: JsonNode) {.async.} = + self.verifyCtx(ctx) + await self.execPreparedOnConn(ctx.connI, args) + + +# ================================================================================ +# toJson +# ================================================================================ + +# proc toJson(results:openArray[seq[string]], dbRows:DbRows):seq[JsonNode] = +# var response_table = newSeq[JsonNode](results.len) +# for index, rows in results.pairs: +# var response_row = newJObject() +# for i, row in rows: +# let key = dbRows[index][i].name +# let typ = dbRows[index][i].typ.kind +# # let kindName = dbRows[index][i].typ.name +# # let size = dbRows[index][i].typ.size + +# if typ == dbNull: +# response_row[key] = newJNull() +# elif [dbInt, dbUInt].contains(typ): +# response_row[key] = newJInt(row.parseInt) +# elif [dbDecimal, dbFloat].contains(typ): +# response_row[key] = newJFloat(row.parseFloat) +# elif [dbBool].contains(typ): +# if row == "f": +# response_row[key] = newJBool(false) +# elif row == "t": +# response_row[key] = newJBool(true) +# elif [dbJson].contains(typ): +# response_row[key] = row.parseJson +# elif [dbFixedChar, dbVarchar].contains(typ): +# if row == "NULL": +# response_row[key] = newJNull() +# else: +# response_row[key] = newJString(row) +# else: +# response_row[key] = newJString(row) + +# response_table[index] = response_row +# return response_table + + +# ================================================================================ +# private exec +# ================================================================================ + +proc getAllRows(self:SurrealQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + let rows = surreal_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return rows.getElems() # seq[JsonNode] + + +proc getRow(self:SurrealQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + let rows = surreal_impl.query(self.pools.conns[connI].conn, queryString, self.placeHolder, self.pools.timeout).await + if rows.len == 0: + return none(JsonNode) + else: + return rows[0].some + + +proc exec(self:SurrealQuery, queryString:string) {.async.} = + ## args is `JObject` + var connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + surreal_impl.exec(self.pools.conns[connI].conn, queryString, self.placeHolder, self.pools.timeout).await + + +# proc insertId(self:SurrealQuery, queryString:string, key:string):Future[string] {.async.} = +# var connI = self.transactionConn +# if not self.isInTransaction: +# connI = getFreeConn(self).await +# defer: +# if not self.isInTransaction: +# self.returnConn(connI).await +# if connI == errorConnectionNum: +# return + +# let table = self.query["table"].getStr +# let columnGetQuery = &"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '{table}'" +# let (columns, _) = surreal_impl.query(self.pools.conns[connI].conn, columnGetQuery, newJArray(), self.pools.timeout).await + +# let (rows, _) = surreal_impl.execGetValue(self.pools.conns[connI].conn, queryString, self.placeHolder, columns, self.pools.timeout).await +# return rows[0][0] + + +proc getAllRows(self:RawSurrealQuery, queryString:string):Future[seq[JsonNode]] {.async.} = + var connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + let rows = surreal_impl.query( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + if rows.len == 0: + self.log.echoErrorMsg(queryString) + return newSeq[JsonNode](0) + return rows.getElems() + + +# proc getAllRowsPlain(self:RawSurrealQuery, queryString:string, args:JsonNode):Future[seq[seq[string]]] {.async.} = +# var connI = self.transactionConn +# if not self.isInTransaction: +# connI = getFreeConn(self).await +# defer: +# if not self.isInTransaction: +# self.returnConn(connI).await +# if connI == errorConnectionNum: +# return + +# let queryString = queryString.questionToDaller() + +# let (rows, _) = surreal_impl.rawQuery( +# self.pools.conns[connI].conn, +# queryString, +# self.placeHolder, +# self.pools.timeout +# ).await + +# return rows + + +proc getRow(self:RawSurrealQuery, queryString:string):Future[Option[JsonNode]] {.async.} = + var connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + let rows = surreal_impl.query(self.pools.conns[connI].conn, queryString, self.placeHolder, self.pools.timeout).await + if rows.len == 0: + return none(JsonNode) + else: + return rows[^1].some + + +# proc getRowPlain(self:RawSurrealQuery, queryString:string, args:JsonNode):Future[seq[string]] {.async.} = +# var connI = self.transactionConn +# if not self.isInTransaction: +# connI = getFreeConn(self).await +# defer: +# if not self.isInTransaction: +# self.returnConn(connI).await +# if connI == errorConnectionNum: +# return + +# let queryString = queryString.questionToDaller() + +# let (rows, _) = surreal_impl.rawQuery( +# self.pools.conns[connI].conn, +# queryString, +# self.placeHolder, +# self.pools.timeout +# ).await +# return rows[0] + + +proc exec(self:RawSurrealQuery, queryString:string) {.async.} = + let connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + surreal_impl.exec( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + +proc info(self:RawSurrealQuery, queryString:string):Future[JsonNode] {.async.} = + let connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + return surreal_impl.info( + self.pools.conns[connI].conn, + queryString, + self.placeHolder, + self.pools.timeout + ).await + + +proc column(self:SurrealQuery, queryString:string):Future[JsonNode] {.async.} = + var connI = getFreeConn(self).await + defer: + self.returnConn(connI).await + if connI == errorConnectionNum: + return + + var strArgs:seq[string] + for arg in self.placeHolder.items: + case arg["value"].kind + of JBool: + if arg["value"].getBool: + strArgs.add("1") + else: + strArgs.add("0") + of JInt: + strArgs.add($arg["value"].getInt) + of JFloat: + strArgs.add($arg["value"].getFloat) + of JString: + strArgs.add($arg["value"].getStr) + of JNull: + strArgs.add("NULL") + else: + strArgs.add(arg["value"].pretty) + + return surreal_impl.info(self.pools.conns[connI].conn, queryString, strArgs, self.pools.timeout).await + + +# ================================================================================ +# prepared statement cache +# ================================================================================ + +proc close*(self: SurrealPreparedStatement) {.async.} = + if self.isNil or self.isClosed: + return + self.isClosed = true + if not self.entry.isNil and self.entry.refCount > 0: + self.entry.refCount -= 1 + touchStmtEntry(self.entry) + + +proc removePreparedEntry(self: SurrealConnections, sql: string) = + if not self.pools.preparedCache.hasKey(sql): + return + self.pools.preparedCache.del(sql) + + +proc flushStmt*(self: SurrealConnections, stmt: SurrealPreparedStatement) {.async.} = + if stmt.isNil: + return + let sql = stmt.sql + await stmt.close() + self.removePreparedEntry(sql) + + +proc clearStmtCache*(self: SurrealConnections) {.async.} = + let keys = toSeq(self.pools.preparedCache.keys) + for sql in keys: + self.removePreparedEntry(sql) + + +# ================================================================================ +# public prepared exec +# ================================================================================ + +proc get*(self: SurrealPreparedStatement, args: seq[string]): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRows(toPreparedArgsJson(args)) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc get*(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: seq[string]): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRows(ctx, toPreparedArgsJson(args)) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc get*(self: SurrealPreparedStatement, args: JsonNode): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRows(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc get*(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: JsonNode): Future[seq[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRows(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc first*(self: SurrealPreparedStatement, args: seq[string]): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(toPreparedArgsJson(args)) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc first*(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: seq[string]): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(ctx, toPreparedArgsJson(args)) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc first*(self: SurrealPreparedStatement, args: JsonNode): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc first*(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: JsonNode): Future[Option[JsonNode]] {.async.} = + try: + self.owner.log.logger(self.sql) + return await self.getPreparedRow(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc exec*(self: SurrealPreparedStatement, args: seq[string]) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(toPreparedArgsJson(args)) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc exec*(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: seq[string]) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(ctx, toPreparedArgsJson(args)) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc exec*(self: SurrealPreparedStatement, args: JsonNode) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +proc exec*(self: SurrealPreparedStatement, ctx: SurrealPreparedContext, args: JsonNode) {.async.} = + try: + self.owner.log.logger(self.sql) + await self.execPrepared(ctx, args) + except CatchableError: + self.owner.log.echoErrorMsg(self.sql) + self.owner.log.echoErrorMsg(getCurrentExceptionMsg()) + raise getCurrentException() + + +# proc transactionStart(self:SurrealConnections) {.async.} = +# let connI = getFreeConn(self).await +# if connI == errorConnectionNum: +# return +# self.isInTransaction = true +# self.transactionConn = connI + +# surreal_impl.exec(self.pools.conns[connI].conn, "BEGIN", newJArray(), newSeq[Row](), self.pools.timeout).await + + +# proc transactionEnd(self:SurrealConnections, query:string) {.async.} = +# defer: +# self.returnConn(self.transactionConn).await +# self.transactionConn = 0 +# self.isInTransaction = false + +# surreal_impl.exec(self.pools[self.transactionConn].conn, query, newJArray(), newSeq[Row](), self.pools.timeout).await + + +# ================================================================================ +# public exec +# ================================================================================ + +proc get*(self: SurrealQuery):Future[seq[JsonNode]] {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/select + let sql = self.selectBuilder() + try: + self.log.logger(sql) + return self.getAllRows(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + return newSeq[JsonNode]() + + +proc first*(self: SurrealQuery):Future[Option[JsonNode]] {.async.} = + var sql = self.selectFirstBuilder() + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + return none(JsonNode) + + +proc find*(self: SurrealQuery, id:SurrealId, key="id"):Future[Option[JsonNode]] {.async.} = + var sql = self.selectFindBuilder(id, key) + sql = questionToDaller(sql) + try: + self.log.logger(sql) + return self.getRow(sql).await + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + raise getCurrentException() + + +# ==================== insert JsonNode ==================== +proc insert*(self:SurrealQuery, items:JsonNode) {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/insert + let sql = self.insertValueBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*(self:SurrealQuery, items:seq[JsonNode]) {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/insert + var sql = self.insertValuesBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*(self:SurrealQuery, items:JsonNode, key="id"):Future[SurrealId] {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/insert + var sql = self.insertValueBuilder(items) & " RETURN AFTER" + self.log.logger(sql) + let res = self.getRow(sql).await + return SurrealId.new(res.get()[key].getStr) + + +proc insertId*(self: SurrealQuery, items: seq[JsonNode], key="id"):Future[seq[SurrealId]] {.async.} = + result = newSeq[SurrealId](items.len) + var sql = self.insertValuesBuilder(items) & " RETURN AFTER" + self.log.logger(sql) + let res = self.getAllRows(sql).await + var i = 0 + for row in res.items: + defer: i.inc() + result[i] = SurrealId.new(row[key].getStr) + + +# ==================== insert Object ==================== +proc insert*[T](self:SurrealQuery, items:T) {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/insert + let sql = self.insertValueBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc insert*[T](self:SurrealQuery, items:seq[T]) {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/insert + var jsonItems = newSeq[JsonNode](items.len) + for i, item in items: + jsonItems[i] = %item + var sql = self.insertValuesBuilder(jsonItems) + self.log.logger(sql) + self.exec(sql).await + + +proc insertId*[T](self:SurrealQuery, items:T, key="id"):Future[SurrealId] {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/insert + var sql = self.insertValueBuilder(%items) & " RETURN AFTER" + self.log.logger(sql) + let res = self.getRow(sql).await + return SurrealId.new(res.get()[key].getStr) + + +proc insertId*[T](self: SurrealQuery, items: seq[T], key="id"):Future[seq[SurrealId]] {.async.} = + result = newSeq[SurrealId](items.len) + var jsonItems = newSeq[JsonNode](items.len) + for i, item in items: + jsonItems[i] = %item + var sql = self.insertValuesBuilder(jsonItems) & " RETURN AFTER" + self.log.logger(sql) + let res = self.getAllRows(sql).await + var i = 0 + for row in res.items: + defer: i.inc() + result[i] = SurrealId.new(row[key].getStr) + + +proc update*(self: SurrealQuery, items: JsonNode){.async.} = + ## https://surrealdb.com/docs/surrealql/statements/update + var sql = self.updateBuilder(items) + self.log.logger(sql) + self.exec(sql).await + + +proc update*(self:SurrealConnections, id:SurrealId, items:JsonNode) {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/update + let surrealQuery = SurrealQuery.new( + self.log, + self.pools, + newJObject() + ) + let sql = surrealQuery.updateMergeBuilder(id.rawid, items) + surrealQuery.log.logger(sql) + surrealQuery.exec(sql).await + + +proc update*[T](self: SurrealQuery, items: T){.async.} = + ## https://surrealdb.com/docs/surrealql/statements/update + var sql = self.updateBuilder(%items) + self.log.logger(sql) + self.exec(sql).await + + +proc update*[T](self:SurrealConnections, id:SurrealId, items:T) {.async.} = + ## https://surrealdb.com/docs/surrealql/statements/update + let surrealQuery = SurrealQuery.new( + self.log, + self.pools, + newJObject() + ) + let sql = surrealQuery.updateMergeBuilder(id.rawid, %items) + surrealQuery.log.logger(sql) + surrealQuery.exec(sql).await + + +proc delete*(self: SurrealQuery){.async.} = + ## https://surrealdb.com/docs/surrealql/statements/delete + let sql = self.deleteBuilder() + self.log.logger(sql) + self.exec(sql).await + + +proc delete*(self: SurrealQuery, id: SurrealId){.async.} = + ## https://surrealdb.com/docs/surrealql/statements/delete + let sql = self.deleteByIdBuilder(id.rawId) + self.log.logger(sql) + self.exec(sql).await + + +proc columns*(self: SurrealQuery):Future[seq[string]] {.async.} = + let tableName = self.query["table"].getStr + let sql = &"INFO FOR TABLE `{tableName}`" + try: + self.log.logger(sql) + let resp = self.column(sql).await + var columns:seq[string] + for (key, value) in resp[0]["result"]["fields"].pairs: + columns.add(key) + return columns + except CatchableError: + self.log.echoErrorMsg(sql) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + return @[] + + +proc count*(self:SurrealQuery):Future[int] {.async.} = + let sql = self.countBuilder() + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome: + return response.get["total"].getInt() + else: + return 0 + + +proc min*(self:SurrealQuery, column:string, collaction:Collation=None):Future[string] {.async.} = + ## = `ORDER BY {column} {collaction} ASC LIMIT 1` + let self = self.orderBy(column, collaction, Asc).limit(1) + let sql = self.selectFirstBuilder() + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome(): + let column = if column.contains("."): column.split(".")[^1] else: column + let value = response.get()[column] + case value.kind + of JString: + return $value.getStr() + else: + return $value + else: + return "" + + +proc max*(self:SurrealQuery, column:string, collaction:Collation=None):Future[string]{.async.} = + ## = `ORDER BY {column} {collaction} DESC LIMIT 1` + let self = self.orderBy(column, collaction, Desc).limit(1) + let sql = self.selectFirstBuilder() + self.log.logger(sql) + let response = self.getRow(sql).await + if response.isSome(): + let column = if column.contains("."): column.split(".")[^1] else: column + let value = response.get()[column] + case value.kind + of JString: + return $value.getStr() + else: + return $value + else: + return "" + + +proc avg*(self:SurrealQuery, column:string):Future[float]{.async.} = + var sql = self.selectAvgBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + let value = response.get["avg"] + case value.kind + of JInt: + return value.getInt.float + of JFloat: + return value.getFloat() + of JString: + return value.getStr().parseFloat() + else: + return 0.0 + else: + return 0.0 + + +proc sum*(self:SurrealQuery, column:string):Future[float]{.async.} = + var sql = self.selectSumBuilder(column) + self.log.logger(sql) + let response = await self.getRow(sql) + if response.isSome: + return response.get["sum"].getFloat() + else: + return 0.0 + + +proc get*(self: RawSurrealQuery):Future[seq[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getAllRows(self.queryString).await + + +proc exec*(self: RawSurrealQuery) {.async.} = + ## It is only used with raw() + ## + ## https://surrealdb.com/docs/integration/http#sql + ## + ## https://surrealdb.com/docs/surrealql + try: + self.log.logger(self.queryString) + self.exec(self.queryString).await + except CatchableError: + self.log.echoErrorMsg(self.queryString) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + + +proc info*(self: RawSurrealQuery):Future[JsonNode] {.async.} = + ## Get all response. + ## + ## https://surrealdb.com/docs/integration/http#sql + ## + ## https://surrealdb.com/docs/surrealql + try: + self.log.logger(self.queryString) + return self.info(self.queryString).await + except CatchableError: + self.log.echoErrorMsg(self.queryString) + self.log.echoErrorMsg( getCurrentExceptionMsg() ) + + +proc first*(self: RawSurrealQuery):Future[Option[JsonNode]] {.async.} = + ## It is only used with raw() + self.log.logger(self.queryString) + return self.getRow(self.queryString).await + + +template seeder*(rdb:SurrealConnections, tableName:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table is empty. + block: + if rdb.table(tableName).count().waitFor == 0: + body + + +template seeder*(rdb:SurrealConnections, tableName, column:string, body:untyped):untyped = + ## The `seeder` block allows the code in the block to work only when the table or specified column is empty. + block: + if rdb.table(tableName).select(column).count().waitFor == 0: + body diff --git a/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_open.nim b/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_open.nim new file mode 100644 index 0000000..d3a74d1 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_open.nim @@ -0,0 +1,85 @@ +import std/asyncdispatch +import std/deques +import std/httpclient +import std/httpcore +import std/strformat +import std/base64 +import std/tables +import std/times +import ../../libs/surreal/surreal_rdb +import ../../error +import ../../log +import ./surreal_types + + +proc surrealTimeoutMs(timeout: int): int = + if timeout <= 0: + return 0 + if timeout > high(int) div 1000: + return high(int) + result = timeout * 1000 + + +proc awaitWithTimeout[T](fut: Future[T], timeoutMs: int, timeoutMsg: string): Future[T] {.async.} = + if timeoutMs <= 0: + return await fut + + let ok = await withTimeout(fut, timeoutMs) + if not ok: + dbError(timeoutMsg) + result = fut.read + + +proc buildSurrealHeaders(namespace, database, authHeader: string): HttpHeaders = + result = newHttpHeaders(true) + result["Surreal-NS"] = namespace + result["Surreal-DB"] = database + result["Accept"] = "application/json" + result["Authorization"] = authHeader + + +proc dbOpen*(_:type SurrealDB, namespace:string = "", database: string = "", user: string = "", password: string = "", + host: string = "", port: int = 0, maxConnections: int = 1, timeout=30, + shouldDisplayLog=false, shouldOutputLogFile=false, logDir=""): Future[SurrealConnections] {.async.} = + let timeoutMs = surrealTimeoutMs(timeout) + let statusUrl = &"{host}:{port}/status" + let sqlUrl = &"{host}:{port}/sql" + let bootstrapSql = &"USE NS `{namespace}` DB `{database}`" + let authHeader = "Basic " & base64.encode(user & ":" & password) + var conns = newSeq[Connection](maxConnections) + for i in 0..=", ">", "<>", "CONTAINS", "CONTAINSNOT", "@@"] +const whereSymbolsError = """Arg position 3 is only allowed of ["is", "is not", "=", "!=", "<", "<=", ">=", ">", "<>", "CONTAINS", "CONTAINSNOT", "@@"]""" + +proc where*(self: SurrealQuery, column: string, symbol: string, + value: bool|int|float|string|SurrealId): SurrealQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("where") == false: + self.query["where"] = %*[{ + "column": column, + "symbol": symbol, + "value": value + }] + else: + self.query["where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": value + } + ) + return self + + +proc where*(self: SurrealQuery, column: string, symbol: string, value: nil.type): SurrealQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": symbol, + "value": value + }] + else: + self.query["where_null"].add( + %*{ + "column": column, + "symbol": symbol, + "value": value + } + ) + return self + + +proc orWhere*(self: SurrealQuery, column: string, symbol: string, + value: bool|int|float|string|SurrealId): SurrealQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": value + }] + else: + self.query["or_where"].add( + %*{ + "column": column, + "symbol": symbol, + "value": value + } + ) + return self + + +proc orWhere*(self: SurrealQuery, column: string, symbol: string, value: nil.type): SurrealQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("or_where") == false: + self.query["or_where"] = %*[{ + "column": column, + "symbol": symbol, + "value": nil + }] + else: + self.query["or_where"].add(%*{ + "column": column, + "symbol": symbol, + "value": nil + }) + return self + + +proc whereBetween*(self:SurrealQuery, column:string, width:array[2, int|float]): SurrealQuery = + ## `rdb.table("user").whereBetween("index", [1, 3]).get()` + ## + ## `SELECT * FROM user WHERE 1 <= index AND index <= 3` + ## + ## https://surrealdb.com/docs/surrealql/operators#lessthanorequal + if self.query.hasKey("where_between") == false: + self.query["where_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +# proc whereBetween*(self:SurrealQuery, column:string, width:array[2, string]): SurrealQuery = +# ## rdb.table("user").whereBetween("name", ["user1", "user3"]).get() +# ## +# ## `SELECT * FROM user WHERE "user1" <= index <= "user3` +# ## +# ## https://surrealdb.com/docs/surrealql/operators#lessthanorequal +# if self.query.hasKey("where_between_string") == false: +# self.query["where_between_string"] = %*[{ +# "column": column, +# "width": width +# }] +# else: +# self.query["where_between_string"].add(%*{ +# "column": column, +# "width": width +# }) +# return self + + +proc whereNotBetween*(self:SurrealQuery, column:string, width:array[2, int|float]): SurrealQuery = + ## `rdb.table("user").whereNotBetween("index", [1, 3]).get()` + ## + ## `SELECT * FROM user WHERE index > 1 AND 3 < index` + ## + ## https://surrealdb.com/docs/surrealql/operators#greaterthan + if self.query.hasKey("where_not_between") == false: + self.query["where_not_between"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_between"].add(%*{ + "column": column, + "width": width + }) + return self + + +# proc whereNotBetween*(self:SurrealQuery, column:string, width:array[2, string]): SurrealQuery = +# self.placeHolder.add(%*{"key": column, "value": width[0]}) +# self.placeHolder.add(%*{"key": column, "value": width[1]}) + +# if self.query.hasKey("where_not_between_string") == false: +# self.query["where_not_between_string"] = %*[{ +# "column": column, +# "width": width +# }] +# else: +# self.query["where_not_between_string"].add(%*{ +# "column": column, +# "width": width +# }) +# return self + + +proc whereIn*(self:SurrealQuery, column:string, width:seq[int|float|string]): SurrealQuery = + ## `rdb.table("user").whereIn("index", [2, 3]).get()` + ## + ## `SELECT * FROM user WHERE [2, 3] CONTAINS index` + ## + ## https://surrealdb.com/docs/surrealql/operators#contains + if self.query.hasKey("where_in") == false: + self.query["where_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNotIn*(self:SurrealQuery, column:string, width:seq[int|float|string]): SurrealQuery = + ## `rdb.table("user").whereNotIn("index", [2, 3]).get()` + ## + ## `SELECT * FROM user WHERE [2, 3] CONTAINSNOT index` + ## + ## https://surrealdb.com/docs/surrealql/operators#contains-not + if self.query.hasKey("where_not_in") == false: + self.query["where_not_in"] = %*[{ + "column": column, + "width": width + }] + else: + self.query["where_not_in"].add(%*{ + "column": column, + "width": width + }) + return self + + +proc whereNull*(self:SurrealQuery, column:string): SurrealQuery = + ## https://surrealdb.com/docs/surrealql/operators#equal + if self.query.hasKey("where_null") == false: + self.query["where_null"] = %*[{ + "column": column, + "symbol": "is", + }] + else: + self.query["where_null"].add(%*{ + "column": column, + "symbol": "is", + }) + return self + + +proc groupBy*(self:SurrealQuery, column:string): SurrealQuery = + if self.query.hasKey("group_by") == false: + self.query["group_by"] = %*[{"column": column}] + else: + self.query["group_by"].add(%*{"column": column}) + return self + + +proc having*(self: SurrealQuery, column: string, symbol: string, + value: bool|int|float|string): SurrealQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": value + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": value + } + ) + return self + + +proc having*(self: SurrealQuery, column: string, symbol: string, value: nil.type): SurrealQuery = + if not whereSymbols.contains(symbol): + raise newException( + CatchableError, + whereSymbolsError + ) + + if self.query.hasKey("having") == false: + self.query["having"] = %*[{ + "column": column, + "symbol": symbol, + "value": nil + }] + else: + self.query["having"].add( + %*{ + "column": column, + "symbol": symbol, + "value": nil + } + ) + return self + + +proc fetch*(self:SurrealQuery, columnsArg: varargs[string]):SurrealQuery = + self.query["fetch"] = %columnsArg + return self + + +proc orderBy*(self:SurrealQuery, column:string, order:Order): SurrealQuery = + if self.query.hasKey("order_by") == false: + self.query["order_by"] = %*[{ + "column": column, + "collation": "", + "order": $order + }] + else: + self.query["order_by"].add(%*{ + "column": column, + "collation": "", + "order": $order + }) + return self + + +proc orderBy*(self:SurrealQuery, column:string, collation:Collation, order:Order): SurrealQuery = + if self.query.hasKey("order_by") == false: + self.query["order_by"] = %*[{ + "column": column, + "collation": $collation, + "order": $order + }] + else: + self.query["order_by"].add(%*{ + "column": column, + "collation": $collation, + "order": $order + }) + return self + + +proc limit*(self: SurrealQuery, num: int): SurrealQuery = + self.query["limit"] = %num + return self + + +proc start*(self: SurrealQuery, num: int): SurrealQuery = + self.query["start"] = %num + return self + + +proc parallel*(self: SurrealQuery): SurrealQuery = + self.query["parallel"] = %true + return self + + + +# proc offset*(self: SurrealQuery, num: int): SurrealQuery = +# self.query["offset"] = %num +# return self + + +# proc inTransaction*(self:SurrealQuery, connI:int) = +# ## Only used in transation block +# self.isInTransaction = true +# self.transactionConn = connI + + +# proc freeTransactionConn*(self:SurrealQuery, connI:int) = +# ## Only used in transation block +# self.isInTransaction = false + + +proc raw*(self:SurrealConnections, sql:string, arges=newJArray()): RawSurrealQuery = + ## arges is `JArray` + ## + ## can't use BLOB data. + let rawQueryRdb = RawSurrealQuery( + log: self.log, + pools: self.pools, + query: newJObject(), + queryString: sql.strip(), + placeHolder: arges, + ) + return rawQueryRdb diff --git a/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_types.nim b/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_types.nim new file mode 100644 index 0000000..32586be --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/models/surreal/surreal_types.nim @@ -0,0 +1,135 @@ +import std/asyncdispatch +import std/deques +import std/json +import std/httpclient +import std/tables +import ../../log +import ../../libs/surreal/surreal_rdb +import ../../error + + +type SurrealDB* = object + + +type Connection* = object + conn*:SurrealConn + isBusy*:bool + createdAt*:int64 + + +type SurrealPreparedEntry* = ref object + sql*: string + normalizedSql*: string + nArgs*: int + refCount*: int + lastUsedAt*: int64 + + +type Connections* = ref object + conns*: seq[Connection] + timeout*: int + ## `getFreeConn` が接続を待つときに積む Future。`returnConn` が先頭から 1 件だけ完了させる。 + waiters*: Deque[Future[void]] + preparedCache*: Table[string, SurrealPreparedEntry] + + +## created by `let rdb = dbOpen(SurrealDB, "ns", "database", "user", "pass", "http://surreal", 8000)` +type SurrealConnections* = ref object + log*: LogSetting + pools*:Connections + + +type SurrealPreparedContext* = ref object + owner*: SurrealConnections + connI*: int + + +type SurrealPreparedStatement* = ref object + owner*: SurrealConnections + entry*: SurrealPreparedEntry + sql*: string + nArgs*: int + isClosed*: bool + + +type SurrealQuery* = ref object + ## created by `rdb.select("columnName")` or `rdb.table("tableName")` + log*: LogSetting + pools*:Connections + query*: JsonNode + queryString*: string + # placeHolder*: JsonNode # JArray [{"key":"user", "value":"user1"}] + placeHolder*: JsonNode # JArray [true, 1, 1.1, "str"] + + +proc new*(_:type SurrealQuery, log:LogSetting, pools:Connections, query:JsonNode):SurrealQuery = + return SurrealQuery( + log:log, + pools:pools, + query:query, + queryString:"", + placeHolder: newJArray() + ) + + +type RawSurrealQuery* = ref object + log*: LogSetting + pools*:Connections + query*: JsonNode + queryString*: string + placeHolder*: JsonNode # JArray ["user1", "user1@example.com"] + + +proc `$`*(self:SurrealConnections|SurrealQuery|RawSurrealQuery):string = + return "SurrealDB" + + +type SurrealId* = object + table*:string + id:string + + +proc new*(_:type SurrealId):SurrealId = + ## create empty surreal id + return SurrealId(table:"", id:"") + + +proc new*(_:type SurrealId, table, id:string):SurrealId = + ## .. code-block:: Nim + ## let rawId = "user:z7cr4mz474h4ab7rcd6d" + ## let table = "user" + ## let id = "z7cr4mz474h4ab7rcd6d" + ## let surrealId = SureealId.new(table, id) + if table.len == 0: + dbError("table cannot be empty") + if id.len == 0: + dbError("id cannot be empty") + return SurrealId(table:table, id:id) + + +proc new*(_:type SurrealId, rawId:string):SurrealId = + ## .. code-block:: Nim + ## let rawId = "user:z7cr4mz474h4ab7rcd6d" + ## let surrealId = SureealId.new(rawId) + if rawId.len == 0: + dbError("rawId cannot be empty") + let split = rawId.split(":") + let table = split[0] + let id = split[1] + return SurrealId(table:table, id:id) + + +proc rawId*(self:SurrealId):string = + ## .. code-block:: Nim + ## let rawId = "user:z7cr4mz474h4ab7rcd6d" + ## let surrealId = SureealId.new(rawId) + ## surrealId.rawId() == "user:z7cr4mz474h4ab7rcd6d" + return self.table & ":" & self.id + + +proc `$`*(self:SurrealId):string = + return self.id + + +proc `%`*(self:SurrealId):JsonNode = + return %(self.rawId()) diff --git a/clients/nim-allographer/src/allographer/query_builder/prepared_param.nim b/clients/nim-allographer/src/allographer/query_builder/prepared_param.nim new file mode 100644 index 0000000..452bb27 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/prepared_param.nim @@ -0,0 +1,115 @@ +import std/json +import std/options +import std/macros + + +type PreparedParam* = object + value*: string + isNull*: bool + + +proc nullPreparedParam*(): PreparedParam = + result.isNull = true + + +proc toPreparedParam*(v: string): PreparedParam = + result.value = v + + +proc toPreparedParam*(v: cstring): PreparedParam = + if v.isNil: + return nullPreparedParam() + result.value = $v + + +proc toPreparedParam*(v: bool): PreparedParam = + result.value = if v: "1" else: "0" + + +proc toPreparedParam*[T: SomeInteger](v: T): PreparedParam = + result.value = $v + + +proc toPreparedParam*[T: SomeFloat](v: T): PreparedParam = + result.value = $v + + +proc toPreparedParam*(v: JsonNode): PreparedParam = + if v.isNil or v.kind == JNull: + return nullPreparedParam() + + case v.kind + of JBool: + result.value = if v.getBool: "1" else: "0" + of JInt: + result.value = $v.getInt + of JFloat: + result.value = $v.getFloat + of JString: + result.value = v.getStr + of JArray, JObject: + result.value = v.pretty + of JNull: + discard + + +proc toPreparedParam*[T](v: Option[T]): PreparedParam = + if v.isSome: + return toPreparedParam(v.get) + return nullPreparedParam() + + +proc toPreparedParam*[T](v: T): PreparedParam = + result.value = $v + + +proc toPreparedParams*(args: seq[string]): seq[PreparedParam] = + result = newSeq[PreparedParam](args.len) + for i, arg in args: + if arg == "NULL" or arg == "null": + result[i] = nullPreparedParam() + else: + result[i] = toPreparedParam(arg) + + +proc toPreparedParams*(args: JsonNode): seq[PreparedParam] = + if args.isNil or args.kind == JNull: + return + + if args.kind == JArray: + result = newSeq[PreparedParam](args.len) + for i in 0 ..< args.len: + result[i] = toPreparedParam(args[i]) + return + + result = @[toPreparedParam(args)] + + +proc preparedText*(param: PreparedParam): string = + if param.isNull: + return "NULL" + return param.value + + +proc preparedTextSeq*(args: openArray[PreparedParam]): seq[string] = + result = newSeq[string](args.len) + for i, arg in args: + result[i] = arg.preparedText + + +proc countQuestionMarks*(s: string): int = + for ch in s: + if ch == '?': + inc result + + +proc buildPreparedArgsExpr*(args: NimNode): NimNode = + let toPreparedParamSym = bindSym("toPreparedParam") + let nullPreparedParamSym = bindSym("nullPreparedParam") + var arr = nnkBracket.newTree() + for arg in args: + if arg.kind == nnkNilLit: + arr.add(newCall(nullPreparedParamSym)) + else: + arr.add(newCall(toPreparedParamSym, arg)) + result = newTree(nnkPrefix, ident("@"), arr) diff --git a/clients/nim-allographer/src/allographer/query_builder/readme.md b/clients/nim-allographer/src/allographer/query_builder/readme.md new file mode 100644 index 0000000..8fba095 --- /dev/null +++ b/clients/nim-allographer/src/allographer/query_builder/readme.md @@ -0,0 +1,28 @@ +query_builder/models/xx/xx_exec.nim + +```nim +# public exec + +# return json +get() / first() / find() / insert() / insertId() / update() / delete() + +# return string +getPlain() / firstPlain() / findPlain() + +# aggregate +count() / min() / max() / avg() / sum() +``` + +```nim +# private exec +getAllRows() / getRow() / exec() / insertId() +getAllRowsPlain() / getRowPlain() +``` + +query_builder/libs/xx/xx_impl.nim +```nim +query() / exec() / execGetValue() +rawQuery() / rawExec() +``` + +--- diff --git a/clients/nim-allographer/src/allographer/schema_builder.nim b/clients/nim-allographer/src/allographer/schema_builder.nim new file mode 100644 index 0000000..cc439c6 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder.nim @@ -0,0 +1,48 @@ +import ./env + + +import ./schema_builder/enums; export enums +import ./schema_builder/models/table; export table +import ./schema_builder/models/column; export column + +when isExistsSqlite: + import ./query_builder/models/sqlite/sqlite_types; export SQLite3, SqliteConnections + import ./schema_builder/usecases/sqlite/create as sqlite_create; export sqlite_create + import ./schema_builder/usecases/sqlite/alter as sqlite_alter; export sqlite_alter + import ./schema_builder/usecases/sqlite/drop as sqlite_drop; export sqlite_drop + import ./schema_builder/usecases/sqlite/create_schema as sqlite_create_schema; export sqlite_create_schema + +when isExistsPostgres: + import ./query_builder/models/postgres/postgres_types; export PostgreSQL, PostgresConnections + import ./schema_builder/usecases/postgres/create as postgres_create; export postgres_create + import ./schema_builder/usecases/postgres/alter as postgres_alter; export postgres_alter + import ./schema_builder/usecases/postgres/drop as postgres_drop; export postgres_drop + import ./schema_builder/usecases/postgres/create_schema as postgres_create_schema; export postgres_create_schema + +when isExistsMariadb: + import ./query_builder/models/mariadb/mariadb_types; export MariaDB, MariadbConnections + import ./schema_builder/usecases/mariadb/create as mariadb_create; export mariadb_create + import ./schema_builder/usecases/mariadb/alter as mariadb_alter; export mariadb_alter + import ./schema_builder/usecases/mariadb/drop as mariadb_drop; export mariadb_drop + import ./schema_builder/usecases/mariadb/create_schema as mariadb_create_schema; export mariadb_create_schema + +when isExistsMysql: + import ./query_builder/models/mysql/mysql_types; export MySql, MysqlConnections + import ./schema_builder/usecases/mysql/create as mysql_create; export mysql_create + import ./schema_builder/usecases/mysql/alter as mysql_alter; export mysql_alter + import ./schema_builder/usecases/mysql/drop as mysql_drop; export mysql_drop + import ./schema_builder/usecases/mysql/create_schema as mysql_create_schema; export mysql_create_schema + +when isExistsSurrealdb: + import ./query_builder/models/surreal/surreal_types; export SurrealDB, SurrealConnections + import ./schema_builder/usecases/surreal/create as surreal_create; export surreal_create + import ./schema_builder/usecases/surreal/alter as surreal_alter; export surreal_alter + import ./schema_builder/usecases/surreal/drop as surreal_drop; export surreal_drop + import ./schema_builder/usecases/surreal/create_schema as surreal_create_schema; export surreal_create_schema + +when isExistsBaradb: + import ./query_builder/models/baradb/baradb_types; export Baradb, BaradbConnections + import ./schema_builder/usecases/baradb/create as baradb_create; export baradb_create + import ./schema_builder/usecases/baradb/alter as baradb_alter; export baradb_alter + import ./schema_builder/usecases/baradb/drop as baradb_drop; export baradb_drop + import ./schema_builder/usecases/baradb/create_schema as baradb_create_schema; export baradb_create_schema diff --git a/clients/nim-allographer/src/allographer/schema_builder.out b/clients/nim-allographer/src/allographer/schema_builder.out new file mode 100755 index 0000000000000000000000000000000000000000..94a1b7c4571e1864edb312e5574165d5b77d3b8b GIT binary patch literal 373136 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%0Aj0jfLkx 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%0AHGAQE&-Og3mcwqIjz;u2p zbH?D-W`3K^Z>#xbT(U~{+%(|HIqePs0tLjvO}cL5*=S5Ofq$*}13A=KOKfV14SuUp z3$%VDpI|fXr*z~u=2a@vdI?F_96@gHhu)=4{0JK2?xlUB6`}TbvAzNFWYMe(jGl}C zWCsacCV5M*&TJz26DvF{6$D%&JNqIZPN#Y}Wp-0EZ|M{nw}HG2It2_)AVU<$8%r_K z)-_v|roPXy2B!ywZj?S~IQA9Q7aS}xfZY;9Kuh3^WImcljcv%=IH=LLvCa)x5P8^& z6IkcQL-07-7Bq?h0Ur%N5(qbvA$oco$nY=Xm?N`QHtEPN25>4ebWRxoj1)M20sQzf z@bhf0mZapYmEfih3V?Nd0{9Lq?9U3|oFS+0J~J|;B<2IWw^F>x1>j8%gzzqkW^2a3 zSwxld**2wG|NL#CdX&~CTjHroi3oh4=Kip%1P>Zkx*Kw6{^q}(VxbhhUXQortG;`L} zoFr6NtSO&S^H|pOBysBRtt4X2Q1({nj)bX_8k+NzeVQ$pMnS-ty^r@G5E|Z1|S< zK(yK@XzKu}IiLuM!apIh@EyRbk@8SEY>NKg?Tpe2UdM6%_Nk~O45>Sk4>UWvN71Z2 zJA!xkza9BjEc+@W-&!w}fBd&1N{)no)5i)UVU&6a`Rz>;kdCyQ!jDu9!BCQsj;sC> z3w>7%i@i8U96dzVU=kihPnsbb?q5`B!nZ{P6i>=rXVdq^V}k0%I`<){c{r5=pQBgq zVum7Px)1iD{5?f%sCl(Wbe8&yEGqwAA-bOMhun=0$3r~}dy82T&64F|9J>W9`M+HYB`AX7WXaC}R$~@EGMiX=k_&1wp$#st5pJwUAl)xeHrUW-+;?O)L z+(-E>ZO}RREpSxDiI%$bYzkK|i7`UpC`F<&r-G74_=mGTmUbbEPKcrlMD@DymuRATs1`mQ zh3T&0b0-Xn>b9oVEC(ns;Q-Ke-35w9OKEPn+7=56jdhDJO3=AYPMH9EP zM1|xdUviEt#90ck?8)AB`#r)E;loslCmq?-zDh-goFOA(M1Br<3xpsK{cZke0Bmzv zNeOs6K^FWR9wYhn3vKPY7PMW=#iz^o33@_ZVA0R@?@W@v#xWWLkL0TF2(0qnP%IMx01Im$aI$64KA5xM{3haim!O?D)%hGw zoG?)>?1Kl6Xqhxw!XAcX0h?ZAPQHv~j19N}2YR5EU!W)ZUR_BLI6T?B^5GUbo=hDF zKW;b90d9vAH=ts4Wjz;kc{kG>Qz*OOg~v7%=@aOPzRBe?>;OuF3o>11oj;q`ah(+CWBOp|VlgWbv{?mY$f zs5SO9^uS-0N992So)-OzCN?Cq0qGPUL#ezR@<%`1{x~Ci8YVa?= zWigWWz(nn(9O=d5Uy%7`SfA(kg~7TXDkdHRQW}|LZ8soB;-)0EULUoofJ$U+Ps&pD=~Z@ZE*1_f4jbiQSLe2Ld_B+k++y{|&?A z(MKlUA7A=9Pts3Sjfr*Y(fm3xz+$YHyC-2IR{N+=N0J_Xy|)Rm74Z9jM=AopLohK0 zG55NKy~SrlN1Y=`1|n=Ecv+a3EzKo|Lm-0sgwZA8c$ygqG0N*r_>994W0x*w? zg+UQmq(qIrY&UInnBS}!M_O%LsP~vQKf!yIF{LGx<@`<7Wf8>*pZ7kq4howAHgM@> zl5sHH(Z2nNi)m(i3o|tV<+ulq6v!xbF>X2u5Hd5Kan^`G3sclQ?ghC|ko-FUFL8J- zwsGm|c52u)CxS1jx@-C4LEIG(oe`Yp`1zxq-T5J%G83nDm8|xE8Z`aG{a}VYc?wyKP|^`Js@zAQC;X=pakl-xpd|OaU)CpoMp81*-L* zY?}}$H3AmKr`E_3W>Vf_Wo&i4C6#hRnz-OTa&j7D|`e;|>Fp6Le-4OAPK2{*& zGR_}wQHOzcFgKcWmvrQQTZ;>EGm-niK?62mhZW6kA{qkjdW#Egs-N;IT&Lfw!f<$* zBrezud;y?x@QXk~c3>GuGh9eRoy`HM?@TQQFp$}88-}b>6ZZ<|SUdzMv913mm=BT*jG-1_HV+4g=Q7xb8a6LvzT0Xx;`7%ju%NazSzc6yF9rEQ;xu zfufN-1cB@q^+t85h;I}UO2%14KhEux)^>FuBLvOTcAcV+6^7(55QcO%RV4c%;3CB& z)@mlT2KFGNfBX56&HoF?eh;-AsU^i#dnOW5dc%IkHr3!ym=!${X%G;{-_{3dVU`>0 zE-vtaS;i2H95n3BN2;TtM#|8DiVvgFNf>A6QBRYT@OmhjcU6v!@~l=kg@G+866P=Umjd zPHTWk6EnI{6}+>79zw0VRyEWajY@r|p$$4Vv-QxjMX)0LpBEjxhz5HsVCEHBp1=rd z2En$C+CDM+AE4+c5u#AtQ6@4te?H3W0XozUO6DRm$CS(iH!e%&TyB5& zYha^GW;&8?b4lDrA1jc!2>z_|4A3QsX3WpY;Vc7?w+bA#-Dq{9d zqbSeP8OU60=p*zGy+)30RU*f7vF(Qu1Q$7~n#u_mJUK$t^=E2TLh5QTGdu6l4M9pa zPP`Z=rcqLMj#7i;DSFe^T6N;vt?1}J^2{)GicUs73kCfsmLyGHbiJ!6s}{&(1x2}9 z=Zi;$G8xS+RlvNq%dfZsaV|#rwflY`dC4+ z?&dsii8s&7j#vNY`Rfd;2C&Ef0Mo5KZhjF~@=fvq*A?n^xpF$wicR|ku8x3huKz4n zE~q|<&4q9MD>w)FsWT0Bsp3I zcD~zjUVsvh!bdDh3aoV9OV^TgE=tZL6Vm(qRCv+t)J#QLS(J3%3v8fJV}w)fEz{DG z>jX%pT<*P4-HGcIbwGdPLI~O#pKXY$UR0Ihu+Z&1UI#Au>B>0AmPPDI#fF2x=hqCR z_-*gQgjm-Cnl0WgT3~DUnSjL%Xo#x#6o|t)a$Z2iKR;eo9F@7; zz)HY-YA1$XpeB18{$~0cwg}vWtpOQL&!8+{iXvjVwv1H`cQcm z{GD};1EzPg4BE@yuI-AgL~zV`9tZ}=*WRHX`B9d(bYvLJ2+>SZOmRc@fJeF!b#k7U;zLow3cCkJosf|g}3RI-}b_Z^L&NPynnv;N=F44 zWp{!;h*(%FuilfraAHSWk5BTnn5L$Da$Qd0iol`dnyiEj zf~RmnZZ80G;B;xu^F{PW#$im{WIe=f>e;NBo5NT(RC9v5AQ&6PZf<%P_y!+aCOhC{ zLwx(3Th>HJ#bhuOz+A3%I~RZyj}23R53EL0C#D$mV|P_X(u9r1(pP28 zc>#MJoE26gc9?o`9e99kzj!^~0OAs^)2Z9dGU-vNn0D(NEt4X)@@nlvgCeA)coX*vSuIlj?y%v3{L?IWQM2y_WyfkI5Mk8(e{L$ zB6A~>xgSX`MoRV^nq1bJcpk#UXqdpxKxSgkLP80&wiSZ(>QfurD%% zXlnDs0>8cHH*1K-Pe|0D57H&Q+>4D3*g_c)M0`@EenYa2MU0CR|}# zrrHx)Eg!0OGpmUMhbZ1=nRv>a;0l+Er$2J!1(hcPKeLLihsy32EBaI4%;LXi)N7m* zPXxh-33T&SoYl-^lFFN7=KA?pV=1r1zh7fSD)6v4@-K6;^6SpalwZ6xqj@nEV63da zej+;(qxwoWynLcghu)tNO^C)f#lIZGP*?7P>z=7 z{n?wXPxBheobrp0$6a}HliV44uiZ`-3k0YsNhCI5JE&vko2c1XN;#!>p_SeH3GRDrNyU6!H z>36}%s8i&if&Yo|t7t;5I0sEe0msVMH6hQ)u~*VGGkVhSg1zKLW$l_+{hvRYTZza-zaMoOWTN^ZYPUC9ZiklX=4 zO0h1-`zH8~`WbwGc0ag_p#!ep*Jvi)%Pu>+daN1#P@Bwc`rRFDc!wuC={o7$K9#W z6Yz=-A76_=;*4&gEz@ZpX%@P>P(qFxF+Jf8bi3BGR&OK3$R zDxGAXGfPceKVZ$$%1NG|T7NyUBx=@dG8xPMI|6`n7N zQ;MeG4XUNhos->8Evh?7mPs#)Vze# z&t=&31zu{L2am*HGf1|Po4Sp)FvC)C%(6MyZ&r5@w#md(k%Rf8^N+FrfO-eLi~EtC z2*w#Q_a$m;L54$pwHpGearPi47Xpk|5tvNuEW3;jBY=M4Zuxxwhp4;1)TMoZu~Qa} zCLX-VXf_FH#nvGZlH`vPa=}X_gsiOg6EemEh+erD1+8EY4UkvMxCc^DEbnUr8#>O$ zHnBoGfI_N0sOKYZID;O*7RJxktspn`>ipbVGXY=^Cs^abA+_O~E*z1eR2s8vF0LuG z;A`WOcPWP*2Z* zZpWIT->q14$(qeIPZY!e5wKSMRawes7cU-$vg+D_22 zvv3T^!t?L5nq_7sn=w+XlAkp#g!nhiaZy*kj=?QV>8lA$;l}yMBaUV<1KyaVTI{wd zUd-u@`$AK0iiS@Ip9!$KHm5~evgqTu)j}5eO)Y92913`~=sU~Z77zDRf`oK3erqk{3Ny?0bJhE*=)U} z9DU%>!SUz=IhQDA7LeUNWVgDdJP&;7bMMF|N6e{s3`iid(sWp5Mh_3^(uU5yha>g8 z(2VIe+SJdnVu7J(E4oIP@Li31@jy2hLlw69!c*J|djpuQe4M{%1JsFI#YFcf1-~fH z)3bvEQ})^rzBSHeaWGWxn$}&=X38!lOQ-DFQ&8O8FD*rs&+(RJz(vjoY&;2WtfF*0 zQL>jyxZR{4NOzeM#z>wwD{LgCYC7_5-v<{!t+96@rI(3Vz<}NReIQ%lFqg8E%be+( zQCU6@&L6c$iX{t71yl3~xz^--{zh4QhR~0-6eoxjVtFKL4z~y7jtT1*($>yE6I5#Mn03* zhIKD0_(!Ip&i8v)s`=d6*B-op-J=QIDkkA|{W@IsyuK%9Wi@lQb&2?3uX1saEHVW^LFiS^V1n96d!rECts5!Yf^E zx?XlYzggk>v0pfQ9}3}M!`Z|xq0*$fT;_opT^>wl5X_2000z;oiJb<_XyE7U-Rew;VFIeq69M6qQ~Yt>widY_JzQSUSU z=%}~ZQ!?s#@r-B!A9d7=b*8m;rb@2Nc|vPH>D7LbD_H&zKs50#q$^`)th0-Q8g_J8 zj2R3`$LmocV~#eHQ#YS}1sx95T*bx^m-83z_70yA*%3r{Ljy`uj-BGrDA+i_HZQ8@ z2U_i?HE#fAOgi2#e(Lc}4$e+T?y#@OABpv~mT{}wkB53~MVAr_$p?o$;7T9Z+5P}?t|CSQfCnc}HPhCiyw zM?4|A=q*G?6RS{Dbuo8Z2*@1SSMg)Kwd`(({}kAF8ngj5&Ylfm_pcQW%mWN`8~%XR zkLAv=(80*dmW$iC|4TKWH*xU;^UHj?mTrhd(OT z3mz{K>z06`V$Ggsd4X&iYk`A8M1S)Pd5!av1A#_fYT1~iq!wmp=(~17PiKYSE|-^r z%Rg8W%;Hh=>hAkFP`sF11pFK*8V*EGfHbH1$hUJOIPGI=n%HyYyn4vH=)Y20sR>O$t zY^8qPtb6X{~D2h&j^wK`~-_deaC>sjK`caB~=HEQ^2g zk9hQO3`Gdz{(;TS@GEV5tqK*t8O(}Q-hP>ZO=Hyx?dM>n(#JKPUFGyCbMa=wt7 zkOjs)ix3J#_mH)ei$I0LxPZZC?eTCm1ge*skNN0NT>eec#|r#=4F=J- zp_^~GMN>rOF1lqL$r*$iyQ5npJ6p1e=L&p!mP=2oK31UT8#M5~zJ`4}xC`!nbAaKZ z22!_+L8R685lP=G1*rdpBMVv&rXM8V_X%@RH`K~>_H~#j@)A(nxQxW$@)F#hI>icIrUrA1>in^csqBZ~Cp!q(A9PI~>hhQBUlX zCMQob^wN=!1+a$ouwOy{l8AA)#9YB|EX14ao_N|EE77;rB)p=oqFT_mwE;%3{0*!?Xga>g1Oh|a7R zy^^aY=7UdX?oo8x6x}j9)2qoEZ6UOG60{kFDAgI;{%-)gJI1I9@b`ff_FuS>8FKE~g zQLj5O(`!`91&A@?_zT3Fl#(G*hL9BV0cV1zH+sR~Pf~Dc3PhVRVU~$Z5re%6Kq&kb zo!zqZx8#C~6h}Dzq^ueEov+MXOrUI+@=o33^>Bp?SXm>aY75LQo!T*ePavi$k_-`B z=3&cXQt;;(d78VEeWlB+_NyA_)7Z$y+KBsw8F&(t9h9?;o=rArqxYfFkdy)wP{}9@ zO6jdV(TXT~O26u!cYQW!jtHVzq+L`TmvCthjuuJP*JTi#<{{XLqA9rnZE3z5H*LwK zSc7uO#OYp{KMW|t;9L-Xj zQi_0QVx#xL25W^s@4VL&znt9j>qlW$md5u2Ae<;wl=fLYpleN2P)pwWL5YjgcbL^d zpY`|?(eOqLsP5XY3Otj6b8oINZ$%aaU@VnplX^*MI`T(kBwG$4j;Vi5Q!Y<2RAVfi zA&+sJ@r#2)5wCT|347sMx&El6pgbTgZK{Kq-9R{O? z0$gPHkharq2&Naq!F`FGf{tGEiZ;<6Y9dSibjQpPiv5q<@&(HOUdxAvP6<&XdKSR_N@)t!@`)f0gwt_<`cuPf*mFLzTZHqzDp! z5hTRZRet#8zNi-O45*1*xk#3CiQiV&OuSIF(t5?GQn{QB1eJQY2X&1EvCZC?0D;%( zphHG=14Kx#>;1Ogn{G!WdrFZU9ze2|umfY#1v>%BhgATL_-c@pg5{8WmWv0*dkbmAuTEEz?BgRM#0FP0@o{p|CmT`m-8t=Vw6A2@JqvObLHK zqklKY2WcD6%HL{y%&X`4%I+*dZ&qZ4 z%M1F2c;-!L$*kOd`j&_}3+eQjprAYB5UwN_3Jc6e!z5<1A`#WQ$WcEM-$tZ`b1{z= z!L*{uW}r=Jz>AC96T7>ejnJC26jA|VL4hDOgaT>qRr>o`9fS5H%E|6t)IO|307WaJ z^0Na6;iI~>sZtktihgs&c%|Q!*Z8+c+ zFf8S7_=jwGHHmZsl1$Vo)9{}tAq`4;f{!NHjY5r9U7Dz8&|s+R4dcq z!P;O*(~hyy8E>Y9sl`+UtP`OiN9QK8rhh_msW~NPM-6djv6j$-FCOlFH5qT^cs6qa zIT7;fUlj)@+@J+}4yU?`KUisL6jhs~e{H3IP2*pf!{LV-!x^tULgyO!34OHf-_W@U zZ}Jbh7@)|4rp~`8y_Usnp>}S}y!@w#RA9nrCdk8S_vtFGrCIQ@qw{(1TcQBE!UhL-IS7Sq-Q=L9X;^C^*e+Lfpk9vacaAmD*l z*hU?iNMIpFr12)9r*$@@E?l>BJwt+N3Aee%{ZhWuDsL%5Sw$%A6QuSXH71Qv5l{go z3?OvDFhR%(Ae2KP1fn@kz~~BIZ=(_06{UqxE|r<3`Mdv-TcbA~EAaJex^WzWi1BC~ zTbGOLE41m^2EXmn4dj7NT}?1i1(Or))JOszowXs=Bjx=_E!qmumTp9%C7GAYhRJ0% z5diC?fkc2rRshtxYn3(5YyxOj0Q{pmfUU zY}$wdJc6B2g1Y?=h0`8_^KHW6LT5OFWp$`8MBJzV%Nk0E+nB`FDRw5ZvOr3eBW^}s zh-(WWbrbh_*+7fgxGN3<=wrAfw`5mC)exmB69RCuY!pMVB}`S`NN)wDXCm5rx&7tc z5UnEvw2m&Jb;K5+br(ferKjuh zeK6qTBpd!=jU`kBINESxd6G6g_Z={IO*cBj>RPGSeQ8xxmB z5OLbI^ha76o~H}^W4^*yPZU+e&6+~-ZPx#%$UxlwpfzlkSfX+&g?Xd7pR2STAqD%{@^ZzjHzs@$;y`drXfNTw#+!lzb&C9Gj{{P$90$&M zNjSiwA-oq!$Wc`lxlw`{DVL(qNNwWQJ^taOAnI?@xR6f7so+y5@2WYLf))uhN6Az#am5rTC& z#;lQ$^Z;YTO)CE>pg?A*y40-QM#hMGMahm4@~1aO_~w;^l-l7gwKn^spWHFWH0j*r z)Lx;MMMKoqDmt|;5cFAipgU*}%3~G{0cj@?n^Q#n z;{uzeYAy1uq+^<-@oQQUJ3bq;C+luWX+d&1ym7eiuuR_}nZAZh-vODv{W5*~X8OLH z>Dw#wm7|vPL{y^0l+7Z?@b|M{d+dSjBR?3MH!= zUuIMH(~)y*VSB#sF3_!kZv6R)R-nXZ+cwKeyo((XUpAqD(~d@FtkKmK%TSw58V%G>T{rrg3ll;iY>SG04dNL9kom)7t zgly@~kSzcmWNXtRLhQbi{Fd6uKnIQ>@COYzE%T;VL!JI=v9IVJCIpoYWHWN0^cP>s z;@pso_ubCxH_0AL24Fo~n{jpm-gxEN*Lv7j8f5&}Fr13a0YLvKuduc5ceGLkP4Jys z1Yq6Hk!T{YC7BoW5Tbnj^itd78|qzU*aH_8_{=UXK09Ey^b~zG+gjyy1LLOX3@p+x z-1l71=gdiuPK+nt)Khf5?L_+-i-h~H^s$0*o{zYz^B5O$xVi%?-NG44I`W{cg*>Ar zBhe0Nov8~xxd-?+#$Q{8SE|VV*se1BW1De4Z{cRvU~UhfT&S(}lOk_ioWsT`dAvp- z(GZPy5AhxE{FU2D`pdMiEn4W~s#+&T<$jX}Sq*2JAQyWe>5s`fuEUsS=&rhblgKX-x5kn?zpL@#mPkk3Lqg42Lp38ROCRN_Vv>Y{%RYf?urN*p2KC`Zt;Z zm8iC({>{LN({vi&?8G+|chlzlvz~vN*D{hGh`35D`z`a@ewM#wBlV1Ga1h2#ZdK~1 zF+#%F5?F0?Qp$M~ARLfFBcuSmlwEjDqup+E?!E(wdxDg{ z>_E0Jf;s^!_W62H-Hm{8e|!fnH)3DL`2>xwq2l@xj!^C(@Z{oY)ZQcjAmZ~uJb0L_ zyUzZQU|_;+k@vqRM6`ls%>YumxSK$X&G`%0UUZ>NeOr`{vrYW&ufIL4jVVMUaWL5p zy8p6K^HO_&vNtD)CMN2>l=bty> z_>1}y+cxLf(q3a#hX=tn?az;r(uiNZ+a>bRJ|6Az34W^g$^l|H>Iq(y0G1Vd4=@&k z0K5y7DgjlH4s--#4dCxV>{{mJt9TKaPLF^K4p6#ygzsA2g>{aH|Os`wkFW9((mGBX`9%Ku-1|tP^6S z+OlQLGC4-$Rbca~!6x}N*i%Ccb|D!S19oG{TZpG_o}@}t!YD2%0T~u;sEsH88c)6y zPs(YWn+n@rh1R%;sgCSTpMbX2dHjG(=-8{pu=R_BzPXkUnJ1(UX#{t zjCQN&AblC+^^wD4Vvd{=@}oBaG8H+7KWYS4CQCM9bBd+Cww*@b93^95<`rfmq-S7b zpX1U3V$wZ0{#n;uf-9Gts6fW%xWJ*#vejVA3{=mt=S6SA5mu|4=d7-3I!ZwTGx+EN zy|LCVhxUf|MT=eXfSyC3TcH-FI>q(my6DxaLp*f`PtNlQ#05r=8SdzjM93gHp<_If zoW|p>AF%!)Wql7Gr|l(DsNg)TL2jf?@E0e{91&8Z0D56+07hdi_c#35;F%&a4I<|xCsbw*~%a3FK6*b=g4CY#j72!-Dv2(_hIXJyCGc#BmOevAp%N^6}82Z{e*U3qJdDP2nD;r4`Jq zc$Uxe3~M(_LF^3?8!6rDp%lA?pfTBxM#FI6fgUSgPV#-hL`{V)Urr8usrP+3gyP9$UuGXu*~yqMQ7!g|T(`=U{xpWybn!ZcYd!t{XWkCf<+48;@+Y@85% z+B(ACn6Nim3vA^9bHN1mj{$pm>}htQ9NnbDFc`MRp2na4v;2PtBL-vrv~f)Kr%m%M z1^Lp273%54*s|RY@L6eN2Y8ygB1^WNp``Vz2dJW;S?(7P(EhC~>^Cgjnah7W0fptU zN4!?VUZ6<8K%*xjm~1>ip4inIdn82K3veYt+9N_*(|E&{spSZOO< z3|iz1at0P90I&#g>$ZE*u}J{!V*E3}gT^7>t_<>ehi3qlf|Q;DD^C z9*-WLTBwS{4bjRFbK^n^GxL@e)oYqGQ4>CkoXq%h&b(V50}BI6zf>%zNbqttn~ppN z$J&}kU*~G2N7J%pabL$hYkO|H^<$hk=yJE|uK{M|h;k|Nz74{XOZ-7y; z=Cbq7EO^f$5|d6^ojePEok{2YQRv<{G-XWAG{RJeC_j+NDPlBMljEP!GhN0tIXLBt z$_2!}?xgdZL{3h3uzVF;SQ)V~!ju1#>E^FCM%&*a0k-WKFt=ZSx_PHCY=G&eQH|4f z@^sNmH;39+cDl(^4n;M}PB%M^F$bH(n2(mNP*9P;baO)CnRT9S!uf|u&9&qQ&Ckxn z`B(3wuj_ebaK+4qgIAbq8=Py-TY*_*)w)Kd?zB{$G4kFjNrQx#1t0lg{Nrq-kjNfySsXH3L7uQ6>TM6=K0f8K5Gza0t%+C_X86Ut#2MkY;-8hW;`#T8UvJ7XekWUEjiF|sr-t6WH*1HrdR@}qUZtCwX{65P)qahHml6U$tu zc6#LqkKYFnbUL!>4(65mgLJaVHktFKBMbarLm{qWnGl!t5f9rCJgH;jRyNY1!>(dW z)|&^amD59%M@(l;pO^}d8zY(7OVOU}qohO!I7jX+s{_?`8-X8qG;uQB6Hq;{4&+n^ za%FU&BLaGNA{0w6b}_3CTru}+>VO(k`n9fUE8L!Nr)H}uij*h z5BV#duO>UAmSq6V`b81?#)(akW!yJol(kG0Yi(D_)xL{K*hdtl$p)2~Q!FEQU?8NA zFCN_(_(0Nu`bx$^*>A6<$IGO5p$=ygx&SZGL2wqoxJyOZ*J9nJ@yy0hcgf>P)0w_z z6rOt^b1-mkreA(jIgZPCSnac$|D=Ahv9|m!^c!Ds9U7oli?9*qjoIvzYt~@JRzl!h z1}IMLZWsYBZ-XJg{TO|)VFcC@;H9xZvT=m>jou%6Sgm=(soClLlQ9+4ntDKPC{FeJ z`>vS(f8Epb7HYR+6zh-rh#B5GSmY)M}!3Z>#!(w~~K92(@)cWdbBaNWi=nnk$YgLJ;3y7up zA-dHC<^kD$gj?oge>{0`HCx)wwuHnNFazxCc1&s#S|98@%Bh`8zn|IDm1(pO|JZgQ zW)Cv7U#_(O&K4H>7)6WaQi?@UE#l05`R)pRM`Gy_=F+}0DQYK3p8z6d$$T<&g1u+6 z0J^neZ6$FA?W84a3d-g3hF#%Q8N${jb;eJ5vr<6H&0__r5B&^Y?Tz%_g6T6kEYDI6 z4z{dlnbWD~B|};bTyW`>u&(~4@g(p;{LH@(g8RWZsgdn){B|-|3jqqvs%R4HrX<+dultW%HknCT>QtMfk7EJ%%&6Y~JftJEFzJv(W`o=5T_d7g<$} z|HrpqWJa41MdFJ16pRgXJ zVgaKIupI*CBL02_2HcDlMZ07Hj)ob4leT{bgvx!XnK`-qm-*Qy?0VtUwK6@>4v(Ip z&LGg!cLu%HAiovxaYjh+`exomcIn$Iqc@p)${S)zTUy`&(nH0Is+MmBiXzu}B6+^afcO!Y{%d6IQj3A6(6HG)d{k zLGq{$mam{(AmX)9j~4!0*V}y?{T>$1Ypl^7i_(#uw{&~hxAd_>4{PF*ua^Z1UBduF z1Em9yN^!SXXJBQzKPbU*IPSM^JZkhqwK-Ab7^4Om;}@3^@Hd%8sHiKwuO7Fd_u-Z^ z8IQ==e$(}6JDihmj za{-g9N!c@1G1`3QBRh@n@a-)Ay1j=*Xm{0o=wC{JH6N$IKPzmn4j#UTdrx#p7V3v& z(Ph;-zFfv^H?+eO`6_n~4B=kqsBzxF6`Fb+3p*l5c|e%{6)|1-Ie=WqN({P|U}|oS zO4rTYY!Ebk2>j5TmR#$NQQYE|rT0a&c;Jh<_9`O`hMl{WH%h@H)Qh^CuuDIdrs=10yopy?(?MDsNQ8%X zcab2Q1XANn`xnG)yrpIuFnZD`NRzbzf0|LgusOvKu$z{GJypSW2El%VVDl&q2%Sb< zvo!(xik=S6n^0_x^D35I3cuGHK}0%yh=@B3L^dl$sO_ zu^8$c&*HlY*?wNMc_{P^BO^9P`tm3f|N45---{8)zZJJaEkw>2hsdd&b_8RAE+>=_6sTc1e$9Wi9Jd|Y> z=nF%g$*9yf2{xij=tV!FtXu}Vb~hL+qf6&0@=#}ULP1mj6A!XB*;rlJhogscZpBoR z#E5GCQDzqLMA81(O83d#2v^fbfedV;1&qNs$awXk-DWsC28rL5iP}I zqnBNWlF2NNYfV4}*dt!UUc%In!SS|uy6y!$!;e~%C!*^sqQXG`SbOh%ba3^kLrIUW zduK7Kr6PU&AyQ)W7jpUrFhhg%T4NDRXNELEk884a|2t?}gLx462OU*nDDOVL1JDgZ zxD6vM=b>^O+t-s zWf~o98if&Ed!wzvMkle+-L%nGf1}?tjbfB0P*>mZS}0AkGXg0YDm}$6uSm_oM*sah z8Z{U3Hv1dBT}}#!Wrv3v1=pp~;igec5Y|emX2}N>cskoXkZqVODj$<~Hcf98YI;M{ z^v%p)1HD_XOVcxG-U3bcKZm9t<)WRtqA*elq5J%nuK^lyB2wkD8a6?w9$}hBC~oak z>pF$M+cvT3xHhe;Af9UNWt!HVfv$P_kImmVu_LbVye^>%oid`i@YVcrGNq@ky7Q@h z)kn-CguixDr<8E{@@LWPNz$wyhT#+NVSEiPW34&B<;jN25Az69e8*z;+MAZe+JJzg zZ2EOsAhk=mu7IE5Yk)?g$l5{6YF7aC{aEiWT&AGqt49@~B2@>_B`p)ANJF~>8~TsM z8RzO=1Q_U4(8!TaN~uWG3*sV|7h(9Hgk{295srbk7c83KoYI{>MiZwC{;}+?L8QxC z5{o&2UyWG z1#I|O*Hum@vbzV7Ezg3{A+;asT#HqoKKRQE|;n>I?+K5OA zZVoOa)s)@?==olPO_%cLBL2mE1~EZ5U=wn|_(-fMfsjz&{W*LzJ0Z&=RhUXS3S|Wb z+8xDL=>>dc^+tWAKXLpDs))&2$nK@MN*%#(?j+QeKMm^Mr%ULt@y{UYue=H7Adn@W z%HjPeGA%NFy9#eK1S=+ZITmW`$S>@+um*$gF!jFG9P4p~^~7)CCy6X|9-x34fDl3#1brahNKcl?7`C6-Rx~)qE8tDDcAixc!A4fN+PHjH z219@m$NOdn^{b@5tp+m{Ytu=fK@LLn;dh;m44n;NiyzOxRpW?61z5cu@oW=cf}fPt z2MenibJ4aHrCT*e1#bhnoX4og(Rm0fK`Mvx_bW@}cW(oaTEW9luTLVJW)w%v+zbCi zulglon^-l9&R>bv_;6JMZxGa*&y*P~Dn;i%k1vJu@E4If41dtl`S}u#Uh_k0Ic&A<~`K0~YX612yw3|UGEzc@d3t!OlHLrB*GGAxtk zG3l?c4zmGt2K#F%qVl?|#u;FO=t!Q?uy+tk)(l{&N~h*NI{z6kwl7jR*65zB8l`l2 zshk>84nshR%6U-6(m7t(sL07|`i3$f5 z^Z`OZC5epqa03j%ZS-w2<)mTJ9l3R9-UkzxicI4Vbzi2O2gAM=US=J@nji=WoiL_+ z^FzETJZe5S^7B{j1NXcEdN&e!t&*|N`}=_Snu4k8Yd*nl2?UNpn-y7WGPOJ-yP>mF z50VxNH?xD-ge)wK7RGQD3*e3pfeYR&9-4l=g!@ekM++!kS7L;MIl+N=#}TP>A~}NJ zW4{(>uHw4l2dwILzYu3yV{a4uaVXO~=j{|2({^m{M6F8s7_FGhnw;iI&6Du3JoXW5PnO~3BXs$X z)N$G>`hX0!vH*)$Fn6u7kDAZ;XpnX|Q;hyqr*s3$2{*Fed5Sb|y+^S}YPlPjfE(-- zcN$HdB6Wk zQuL}XF}Ai8_nR}arML%%O;E*$tKPzE4x1VIJR&;(P&^cJ{L9&|rMN@%vd8coi0SglD##uCYQk zTM#r*IPV^@+L!Xjwc00)L?T54p`p5TZJa3cIsVksHZAkYV-^Sz*Ek<817@qlAz+l) z*i^;riXf%`v9NE(GGL)qosUYw9=k_0aR=2Cq@{!Hm#3%0&*=@F9Y9Z}u!9oBNT@Vq zSbe2mm_vQczw`^2dtxJ%`I~hJ^XvHIGXG`VDv~KIgKt2KlDnE9ao+l;E`SjGoq$Ug zYxkpDKhv)b0o|-xC}KSnviuG;CaKkur1^; zbUXs!_jnG2N=&=bjRMbqf50NV0heSQ`G<@m`e)fQ#}=j-FC%x8R4tL-=`0o{#x{5Gyb^x zaMKRZ2N|P)&h3Pnjx74v)d$6Oed2n@I^b&NBp6TJwUBzEt5H+kkXu%RukuvThw=G$ z3dTR>kBjj;BY?4l5`YwRn;2KS7zcz3nOdRFX3)|v9($og4)%nDs8T8m?50dQ!*G5_a9w56^{_ARnQR&}`sUAM#g#POL$RrUPscJ7!JLMxh( z4)E!N9ElI%MQaVsB0EC7%EGhUM}UV(FdN2Cj@h|-jb*_I1-r)~m z+8x0^**KE8^s%@sxeE{sV9cb&`fR#!8h-l)4P2{fQnK8Wy3nzmg-`ngW;Qd3jx}H@ zJ=;cy= ziBc2Z0BlDs`dK4E6*_@etrpdal1nte)lSJ`YitGPPb(ujMUv9>i z07;lznwJ?+4BeerbkD-`k;#PxPc6SKdIz<X=BZO-m9F>;aqS69i#;RyNd_3)&*qH zW%h^LIlmg%XK$*^1sD;+U5;}ODZA(c@K5k}tNwTbn|V4^aPDl_kGV5xJ9dC5rneLa zHBFVIe-S%;2jRb?hU17{ZU-py#rGa2p`ji%@ZV7*dE!s`8j2+}tCq;YsCxaV z5&eiG(XBF;$o%+XRptuwhUBn9xa_v-knv-JWehkM}@tKOpFi$1&dR z50lJJ=XMXY^&zAQ&}d8e+GR}ooY_(obomzDGEN>~@T%V+Vy|~wjL{L0{rQAu-=v-f8ly#26nIibB8OE^{E{;%;GQ)O;(0~y>ir0JL zDMGeC%%GFmSo0FV2Gnk6#uZfMVHe?WB8=5P=Z^AWDG#n?DU?`Za}3T_46er<0zCL7e*}OqvH1m(V0=)c7ctA>x|2inHI1cy{FXH0 z*9f&qb-V-EHp7-MTViM?fR^!;A90L-CSuiI@J{Kp(;QIj#NBW%Y>M|M)xbkJlT zw1v_QPhSqzdp$%qlvzwZJ_CvRwcST@SC? zZAiekJb_B=oVc_D^_Pr7G3wQsMG_+K;FsxQS*BXOiv_I*v%@Y<8#_3f_&}$z zosXgvVw*&%#Tg8LSFb3U#vvLL$~>Pe>kGhO%Lo1~_3WFL>K?p*G*?$SAzXO*}Z9ikXXD72fUz0M=3 zh$E_41%1&UamTygXhjn%H003Pqph;OaB01n*!UpEe@vos4A(!jP7O=p~8vf)-kDRunT7KxFjZiST^tsPuwusX&xiI7_ z0){uqDWC_mUC-XO&kg+1V_(kWNs?6(sxf*KueZ3T8fQR;v%Cq!KG-u@I17l9*BOzT zd#IMt6Pyq8HpK`1O-JsL>4!-XjQq;`Yq?}`FqG5zl=83HKvUanl?V#dlHHmb^Ayha z0Mur5M3~@V=LsQoGx+0~jl(hVbCKsf2ukRE-A%p%gj$0PK(%40&fstSs<_mK|A0A* zp<{%6V_P)wgb}`UjkKmE7=2VhKc*XEIxi=6=|~akAw1#M3HTf%p>}8!?@DGPP29KL zMv`B<7B(6jdRk$ejcKRAFb2m(nc4S#Wy>37o9UD&8xN3&7gFGMVaQNc6qWH}<&6wF zxl{<4(x20H|8A$Y*d7lbOXT*f$W4ZB17RAJQXWgCSBn!;z)%TEB+XT;!yZChFP^G< zm_KRrO|1W=^gS#PD@K2TQ?-DAvv*dQ)lrR&=#f(fuSqUma+fStNoL`VZ9pY{JS|G_ z3|zlqmS|ijR?}GD@+&YrU6BTu?7rN!Rh@A8Hr)^mn$IjGSrJXZD z!6OvN*qn>GejSM>Y7Lx!2@>XJAgQ|qBP^)wcKT)H^4XGtv%WU|N@*o@QmOP%NhKK( zfN@?iwu6tsn1hcz>TqWVpVRf8RljF?s%iW+Y20P^*uhOFkRh+@Yn6W8KM}K4JHQv< z9Ck0-OKYUL(lK9Nn=yY5k1lHg#Il3O8-b=~gqsqR^Cru5$C^bx-7z~uD3D-0`_r*&9)i`+~{>hcDL zH>@ZjGOWm>s!`q;sBM_L5mfwbj1Tj+3Xzq>O6Q`3u~w1*D2O zfhjaB1|~h0Sps}vKXFJ@DJnYT_>>B%NH7@WxA98!A}Mp3mAIeZW_E$6KBrmHo23Ls ztR@kL`Ma!t5>4#mD*_Jfhl!l*V9gYG+YCh1VA&bS5$=4Kd{0O2L}rdEtsMd`#rj#m zu!xves+dN3z((6g%V7~ylw!IMrze3oEDSNRAe_X@vD6DOrTBwuH|3jfa(kKZg~jA6 zP^AUrjY)D%noM9#Hul~7)h)iPXi_Ro($Tl$@}mu?MJRIrSyEl$8|0E_TQZ8zbCyy6n$;`9gHJiafKr^ z7zab?fsjhwRX3X#QrJ_^?e<2PzG0cbF^o?>==m7j()oVre$u94(2;u8%p&RoRUF8c ztyWJW=EY=3F=CC-a~^&?e5sO+ue)#Z-?{FhRzlw1aZ2###b^KToV7HayW+k5`Mzcm zMYMk*lIVu2XRQ4;jz_JK(~)j{?15FtQiX#%-)pFl__95|Kq@zy!g6E}UXS4GRIDAM z>1VMW^zRJ*W?1BZbsGn6wMU}6oYT@PBad&ds3^(WmNl-4Vnl?aYGXBaq2&MIYNU0L z#tdr@(~-0Ev4S)%M*Pibp-40?n+LmT@74PQzyHX;(~%u46xqzqx$T_xc&e$x%=jCz z@B;|!HgFXEcEGuT5k9k<+YN+)1hd_i^W}78)xXe+&hOvX#|rp9sriZOPTRvFfTjs& zyy2_-O~w2+^UCI`wc|E}w!ofdba+?-vnW2)*^o!-3Wl>f&KP%^%a$@??7#TSHO@sONLB(Sdg1kk za&f|46?8Ay_2oZ>UF;BfP#KC7cHm$kX9VC{-NQkcFy=VaydA16;@lC*IPqlU03^-; z+_C!C)B4vF0z5GZW$|s9{xyw%>Fv4NU}A=Wm1yKA80x*)tT`-!U00@X_?4AWQ};$x z2GGSNKrv_Fhhw}CPCrj5Od@GG$wV;Up(@eDU7Mk_VgvKRiqpM{Fx#Cd-(}A5eM90d z*qX4RbV%?_t~`E~^;&QyXA_PGk~ew0$rQ!|GzKqu^9IWx`imd_cIdBsLO79T3VP7( z+`W-N&YN18Iudfes%e^eV?68c{{tVA31KA{u}#dTV}O`V2jTB=DV=L+R$$}&UJp`X zGPd_ygfW#SR(XtVo3iGv*hv8vwu6No1_C?wa$L$3Peq=_#3`Qof9$;pe3aGo$2};c zXw{%ai%WH|E}$#{5(o+!mVtl)B8%dJ5RwUrWFs?StJo++i6I(WsMM{vw563=TG3Lg z7}wBhHCj~Kzim^iby8@J%fC_t=KY>??=#OclT0G5?fZYw7g9769j0-Bw8 z9BH*zaN+@iZGp#$hd?!4r{v|M}U4g$K;ZU zHzIS^cj!>>*2uwFQC;mtrXR?JC64zdv@ET5Y;SeK%bPVpKD1SPv0nYbrqM&YdYa)6 zHMtlm#h`YM%R7q8KbYn){xGd}zp!Aqu^JwFlP8Xb8yC@`d6UL{EiTH3dT9o0kk}eq zmA7)PkT=0Y8KFk6C+0(`bw3pXlRFU#rFx{BySCOeF%}2>aS0sm%1{mvbtViGOK6UG zaO+t9qRMN}Y}s+7^eK1g*|2pTWqSM@|DsI)wlbcPPGmaY_s`38oy2@7GA(df5g{Sd zM_!NTu5Imct|epj5jyy$ufBEz)%`twy4ay4lBR*Y;^a9zb#jL~;j=vfUh@Dim|J@&dd&nXlATb|`i1#j! zCnnOPs^z%3YwPy~5%EQ?&GKsv*oTDoiG=qV3Cl65t3yzNYag|GgL z5*+KY-H8Mr`81xXLzdus@~zrKk>F!OV62Ns2MHeG=C17ztT||O^wS`s zjd9YMD2_H!JnII58Pi8e_5#hvblHwaA5U=Ya5P765gFKhY}P4czh6{mmb04llqJrX z<{;<3Idy-8B2R6X+14Nb8qa)rC|;iie|xQr37Vs4aH=JzPzP+@>2^roAAcpDH+p_s zmuuJYQH3$vg|a^LDPXewVw~x3U6iF=_!|0KYsjv*OR|-CnJ6{QJBCcgS!={6v)jLo z6wQ&Awu99ylP$@fZ*OGRYGnSwhN~e8PgZJvA5}|f*ctpjix8yunpS(EP&E7d!m|9h zJTZQ$4mI=Cb;C9L_pdf#O`o&D?~`03uP?X5U4#Es=>^yM|3WA$IXW=D=y_ogL6VQo z%lnT^AEGzh+BEw*m-YVAWg^)V%M_k8zC7~{7L%jDkRxIjVqBC$m>_1I6VR8=454-u zzll8CE|PTX4Qah0t@9rv+}E9WteXkcocsiLC~x(dyS1S*^Yl`Bc zol+EI?yHv4%e-H}D03F=W7~};wo6jSHjvl`#$#(2k<^bVFtHW7vHb-*xqBoT>`Pec z(8&s8_T8Y#ekx#+y>mY`)N7n*%73OO_}1ch%hpLFJB>rX zZ2k|qk-br3K9olGQkRuK8I8X07jdm?4nD^-WMTX)75nxxPC}vvK#rF60y7TPG#EGL>f3J|T@Q;-g ztY&4(bxKPANtT%>l+vTw9oILK&Ob2(DUgVbXgg87*FT8AJ!Hi@ssqcqdi%3G*>;qR zv^;sP_P6;s3&t@8Ryz-Gj;k9^U$1t)`iW>~Q$ifBC9#D%;e|N4D&l9=*7JlrBRH$A zYtd)6pPjSNoR)z61&;f#Jm884!RRfN*6nHV*e<#_@_K%#U07_*nJk-8EiUPf}1e$Xzh0q zqViz{g2*H~LlXP-3t0o(bXuHBH>Y2Jnmqin^AOFNF`r^Q(%QP{Q23M>;!`;3-Z_dE zWG&?+9v9(-ds*YeI;{(X?JMWM5PV;r?!sZYH>Z9=OE1yfQes>ae4j|)H=|8H_m>pp z8WWpKx4oAByzm1&TPqEpi^-Oyd}2tK;D(~ovA*p;&v>Y6;K zSRVPjyD;oiS|qbDOfn~XI!IL}i_A^uwslhcJ)F-Y;oX_$3DZHkEkt|eA7D^-f4P$v zeAqQ+S{$cJt=f}~+SUs%=G=p|<}yW9`8zW+6i$o!wHo~M+`w91DwC(uk!BcP717Kuo$a%vet)ro2+Yd!2EZ7c|QqUU-TM?u(YMFzmR}HPf7A z5gA58EYU$I;yX@VVWe8Ge)%AN3Gxe_AG*Ra|ft@DGe!Axk z35I};a?HeIGsm`3y-iA@Ay*pW9mK~~vWE0ADK*XE4S@#{1YOa<+6Xxl`gG0XO~luP z#GE73^ZcLUEbfmd{*t(Q&<+8ljd1X@34S4i5C%-;x@5%+XVWZOm&09m&E*#%CP zwGTkZ=IB#0kk%zKYDz*np8hNN2~i!`2zzF#kVc;h%n z61IMvpE+@?3byxXD?x|dUCz-9Wx~#@nR#jl8vHeeN^C%HbD6>htoWE1umL%yWdHPp zax8&WKRs4^{WlXD8TlX98fLjUWzXX!IlXqdZvV{CXyi;ZH$F7FhzJwp7(kBN`TEyBgP3`Z^du#N*~?^@ zksiMx$8{auioN6SQTx4f6F!4jELDLW$BCxlTuY&EKagX|O>)I?6LI#{o=$f594{Ml zQh1UaHt58#DO1@vXf-xFTb|vDyqNGtWOfSNVME_u>#~s@XXD zFJ!~!t(TV=!`h`NdkOImA$CVz0V!Os!F`Elu5*LSdDDpo_suI4EMBJ_Yq~$Ckum;| z>}|G&H2800u7JvtqM;cjop!ggxxfkR*g4zTygi|B@9k{<#!!f%3z^Flwf+@ZQAf?0 zBPOQ7pXCW`Em@Q|iI(gFNwyAMcIZGGxV1yzV@=@Jc;GHNn@__`qubg1=SHU3c!U+^;|jDWooZ6r|y;Lowp+zBsW3m@EC4Y zCU+2P>kHYvHMyXTa;!zY*ZAnLiO^}4>4~UX^|C$`cht?6E%R!lc9lJ{-aSPRIyd6S zOC~uHcc2YlnH;`aRwq+bv)^AM_SY>mDT8}-qqZ}^#K5GB4eM?NdrRwn9qYKJuQz0L zL;1*87YFmq%(-FDCXq(nKlJln&*s#b?MHR@6sy9T6$lOfqg9+{8lk~|?ISAOH3+Q+ zYU#u^isw~>jGCisb+;edk(hxIk^!S%TZo_qdeN_q+j*`r zKQ5~g%%I85xK6>hNWw1=ZQEW6>oPsh;ZUT|peJh)R=+%(&J0Kr2UvS0{-&nv|A}E; z%k6gWD;234{2E&c5OH(#aA`cedD1V#JM!D<#AfyUOCkkt=(O6WZ6YR8|1g(A1=G~bM?EVO1Xl#%#Ev6c>R{?inwuAPm$0zF5U{g+ymzD`Jq$Ha!4F zZ<~K3*0j$|E`AOHh?d`fR#SL^qiPjbiPM+p)&z zi&D!m^KpN2r#kbv;aKwV1#_7~XAaPuN1qglrO=sYL}#S!?Otb&cT&q7nuB3nKO!t|a62^0$WILPbmY$Jxxpgz;zTCPua_jy;!$F6t25Ym11$@i2@z)bY za?KX*+`0zsH8uEqm@?WxaUJ}D7S|?@8&W^3&Gf52k3Ue!Y`b_&H#OYaYX*aI1OBto=;*jMtZ>zh$Z=z zlV+!ut(&z4dA4hd zQsHK2O+wt+G<32VsQ=Sz3+|ZWbr|)g{78XbL49_y<9sqVshv;eJ-y0w;dyea2&L!2Z1*~j9{qWWBzKFF*hSm zJ?7C^%%WFqtb>zdrQ&+dPjEUh9nVpiWn*w8Vy1|x^Z%dufOxl#MrrL?wolQyevn4d z%es4HOB=2I8i5A?kncz!#}D%1gId~aFyJ-PJazDcTuA#6U7;NXoj0kOX3YH09K!lX zH5s#W&hE$1W4IshFCooJ{USX)O`6o;j(E>Gt#-;$gdpYVgft8LrP)pXWXC4&hWVqT zzn#Z)ZXcm!?g}%r9J|29<%*xL-^2_~+h3264BO*dyk#Sq)tF6@ z-1?mKL+p`s<~%#{IKz`9>j4r!`e2Wb@g_M{l3Zbu%!YN2)AFbTUQIkZ(O>^olgg9# z_5dR9R}O2l;(6|TFAV-J(QM8YWmHcAskY99QyvYqk34cpV!E#;>t%WavqWtZ{H}1x zX_9bzcOWVdqBMxeqQ14eSQ7Ngs{1g$DuZlw1C2c?2ie|aGV#=X@l3R8dGJaCxBep1n7X+S~oI za&}gYtTu6qhjk%I#iR4so)a;Sj{bw$x3+9er`Eg;wzg#=8Sz}y=;jsKz04^)mS=j- z#;xHvTGwub$wWOl%Jb+}k!pi~gni`UElCUZs^#>m&RpT`!Na5wf;CnJAO5zJnw|z_ z3PTT0(#!e=jYeC_M^z=9Mf0CXcS#8iX#{dV&{&(O~iNO*s*zBd&0Cbguq!Azr7-{8|I%xRv+rt~t7 zU2ki0j1((^xXl#r<@efRQDq6q8@nvN%z0za2W7Hbj-ZsKVJUj5ve7|bBco$4HuAQ~ zwBKWrY5NFKtA=t)UtHgJb+9ZuRJN{89T`393c71ENx{6*8P^)lNQp<%vC=ekh~zn$ z?qii(t$7u%5jM*!ESF9`4lT>4u;w74QLOIOHkUaeGimhC>=xNEMq*UUFCzsJe^%Gx z*Vl;uR;BcGBOq1PveD>u=e>O?MSV>)lMDl@)Rt1OQ&Z||DgR^!+HQ7A{xVYq@43rr zo?DUkIgjesC$x|1eE3DS*1C4i(ngjSr)E)JK%beEl*{kCL-U>gH~!YgrRqi>3M9otjN?7CdAw zx4}2AA?-#s@vpf0MAS2SXtglw3-F>aIXSIM-K^-0IMs~KVlaq>%KLa)osS=Z{a-s-&CCqe3i zB&E|YJHRF(`P4jbrCZyS*3i>LSTkRqHuxXKy+}azQLk(@>qa+nbXBXa8{KG+u4=WN zhYQ#M*Y>iBjm|g=5}tm$r|mruM~ftu&udO&S9z;vua}PFjlnM!M^&9HPx4H$RxcB} z1rn_+^Q|sb18kRCZjZAUY?Y5W)f_a*OD3g{zfCd~IWMRF#0jHi&8R;pU`{O@@_RHU zA*DWaG1@+$&LtcNDADTPvIhT`oaEvY`&zlnrAhV1)EF&lCo8)2$+Kv=lCi9KTQf{F zQmbY&m#DmUTC47pS(H}YtFBW(t>jLM*-@l+xmPdKQ#o8kA4G!0)=tXpISnVajF-&H zm__bh)1>%R8%bpon$+K9%GIXsXwWX+h?;DWT0MG|#@8CRDyy2lB+uW&r3fAU331XM zyY~W-?TP;oXppCBu^vznOl?IIqMpZV&12!Qg;%?9zxKub8c;6n4hXp(yUj3p;xJ|M zRdTRjt{O6r(u_1mubPfCM{;8uz~xSEMD7^;{zQW=k;9HF+ho^fRI+mBNG&_>RM-!g zH*wtvz17*~aj^V*{r`=J=+{Z781sxi?c#E-xr`!~Rw+pa`zVPKe{+=0^s(pz{O4q^ ztES^36IJS_qg#iCx}9)hJ38Lr-)=5IN9uR^PC>s1{xtaT;(Z3X#`=NMQp4SE) zjy#zw6Ml^M{Flf?M5Eg~65G3}N-4|l;Y-JAUlDh6$DIRDNOZ(e0meUOpY-i{+4W5h z*n9tNhKipc0$S%!OzS*Rh$~e_2@^ATV@b}-!2R-JijE9i1T8V{K3?i(lTJ)~a?5sl z$?aZWT5VssB*+plYNFTGDc*e$pIC`3HDnxt_hlhjWJ&e0jlJzP!fKl8Buf^ zKDoQ&8!qske(W|$k_dWf1kwVHldFczpgw4h z{zk-0P*1LOrn^AP-`|^@iHeDd86+!Ez3%c#owWT&Zr#hd(jSpY+l|KRa0Fl7ROu(| z{8&rh+`rD?sl&{3=cCO7olTP4+H8(4MrzR!_XZ4 zn~8F7LKY=A^UToP(Merl)DBT@-3z%==8_vsy6nNGFIzWNx;g`$EHwCks$x&I?s(N+ za&wniPP7;9wU-07)Tmg4mdk{57{6x>tMn?|ro4KTP~9L@vQ>%3B0iU){kDIQkYsbN zS=r+f+JV+`c~9wj*2FaUs|@dwqxMEyKAm>no?vlHbf+ev6==&HFw)FHnOa+eTQ+EJ z{GSe$I&h(LIJnkz&q_&-D9Y1pU_`vSY2#boncs=>9X2xSfxwotq~{~ zC2jiuTke$@SFLRE$w5NFmX3+3<7kZDtVj2( z3XOmdr?Vx?@EKzozQq7^ohc}0t@q-jW%K+R-L{MX?qC1Rl$kmdx z+8;?RA*g)4yE*u=L>fJgX0I;zae_Xj)vn>ys+Ax2P1C?j7iF`I23J>qk8sxo2vv8r`hkgZs9I+0_Cce`^`zToTVK6G;V<^wUe08t=b4H z3<2Yv#0%@Sc8Gz>HkT=y#6O58VGH{qQUZU`26h&4ohgrvd$3)CeZ|~2YP-SxS>|50 zjYq2&NC+8sn2yy+avP9a`|`a+bT&Y@o#O4 zVr;Vf(<>E~FnxHFZ3g{0iVsHdcgI&FIk9hi+qfzFd&pke=F(l9a> zPLkJDSX1$x3rOW1$-bTMXV}p8v*UCq*ZIe;^kY-1ea+qlI+a8{w#&r(cSyXy zB)-rjZr;MP$;UX6mFII?4gS8Xcv5FpH<>U@RQRHq&&b-W#IIuoTjCw2wAy`S#zu*r~ zQw&K(F)G!IdDTa5LlVB-=7?kRUlV%Io=WbB;eTAU4N6gbW{F%o3yClZfS{W@mrC09 zCTi-}qX%fRo2f8vVO1|k+djKFr$H6ZsP=RjrkUcN2p_3cTNluHRQt&2FPWs-&1q|P zxfOctlMZj&-;XicEgCyX^2Yhb{dAaBrtoC8C~EzqIjXPv!Hx&uLXK;mW`a*O!KbAP z9A0WP@*!m4o&E_JH#KfkG#|zsFEqc>lEt z{;CQ7TB_jNZ15|3Y3x5S!5>K#yv_#ys|o%M6MS8&;8&X9tDioK6KXHxzeY+sk7C(g zEDDEStR#0dH&gn{7|DFpr&&*j=DpNo>B-wmDL_5X#^}e}vIeig-&!LP+3EQBSAjmQ z)AK1^T}!Kd=V*;E9t+zp$JtCaduh#(BWZN_>JXQu02}=U? zrA~96E4egm+j+=KJ#5=kvhDH{$#IGaNm;m}uMym>r;whB@_tzi$a#{TX6Kg|@~zTw z6jx*0p3Vwjzjz}!fBGD60mwQlEA?LPrHF2_R0+P%bUk3DZkYI+DUM4!WulZtDhX9T zt#%H~SpIFC<(@c=mWYBxg*0h7n8${2*tIDRVOg3;TO7OOyGzc3G5tWjd8x&*LGqkX z9NzrcC0@2TB>!C$hrWi*Sl{I0nAB-@F1&!^kX3z+ZF8kis1L1lB;xg9eD?$R*b~b?eQl$%G>Rh8Wrewfk*C zM4Ekg_}|(NJY3_F+T4EfRWJJv>0)6&aXF^9=}N0TRgL|6Wzu|r*>nVZNalCnjyBZU zHF)bqZ0Mp1YVy-y->Ettr5>kaFFRE&`{e=&@7r{nu>Fp%v+`4l#f}_*q3=zM^(M7C zyZ+Ly-CW-yq21c?crUFT`qZwtbdA*ti7ugbJm?WoeXTABwY9@64aF36Ts+*PljEUI|Ql!`162a~WFb3PYn&DU;UM zBt5W0(vgIuwQ>@!n55fUQHRDhS(e?{wp#zcUjDZ%U1BQM+T||m_3P6UE!H_lYMJO$ zXBBM_bEYctXBPRiT_rBvLD!Fe5$6hUS6YaCL*(((oC`b?ewJT5ILf; z?ajmCi7&z@D#*8vP-aY2>*vH@Dl=EONxmqxfOUVKjJc-!=lu~(GfZ`0`eymcfnj>O z5zXRdu4o!kXW{(Lve3t5VSb!T9cI7b*DUB$RxQV0Y8JlfGGMb1^)e9eN9;Y9EcDbY z7 zXKj;_XD0+{o#DkT%&tcyB-}69ceD~Cl#~hYZKMVJ# zYY~cBHlu!O9WvV<%x$g+sM=Yg%(i~%HICD|&W(b;rrx}CycCcb;mH>su4os%HTRuT zr`3+|1R;i7h@tIFo{1B2n{6R0;l%oO)-8#b5&?F30$AV9uDEaK6<%5~>Zz6u{y$W^ z#Ikf;eLD_8vTtXX?RH$nj=#3m5|_+pcH-OFW)qnudZZr==xqe8I(z2fA`&L^<6rG` z48Xgxj>Z(T_2T^0%U^I;kG6qiJYi8{v@t3Y4Q`Yfd#}4#e8aMkKtF0(j*z51O~eC;ZZV<#fb?-*(BjZ2gK=&52+cxZ}9tm1RC64RwmTE)M(OXphJ$!!EOM3Ywr$fybn>>^F!=?C$@*hkzNoG8s|vENN+W;l&7{bw9?^aX$o1gn z;qTp6*IqY!OHo~LD@O7yCrQ53U@lWkQC-O?`Y>Y- z#h0PZZ?hC=-i>~aF8Z4f2&cKTB2YoZ;A(YR~<1g96dp@fy>e+a!6~E9{P&ACh3MfjiLgaRUq?0 z-qQ-!1Xq*(;3{QSCO6JEmnjtZF!+x4YlEvg6o%5i!5H)ZU91u85CIpaJ=XJVEJ`dZ zxH|0{i30v2xmhD7IR4Ix)cTShk?5L(8{t-Plf|YR)h^Fool|eW4%Hy-hD4TAUx`mX zyRS}CeQto25(od_eH3-)GFFysvG_(Fqx9THhE?|^Fml)k;oOi^kIxZ&+^2}wSdV&b;b~mkl2%<{Pj;CCjquD#P zPI$t9NmBI25J|U79%|Kjihrj#Qo5byn>3rF&oae{3tN}nzCeqp=X`p}Z3b)ubet_V zQjqak``a2=ZUiwfZp zd%=ka<}yX)xaVz@>9jg~4&$<}qIWE+f<*>`X#_0}rc=@Ok?O9a|h zFWB-L&pC)~9pBWgVP*-{!ZF`Ly(SpOwov9YGTs$972Iy2q1XoB8`Yn#5o8BYjg4Nl zc+@QG^4Qgp@X_*XqZYz?|E%D;sSM`?@cawFxr;g4>4atcCx*4Hz4dx(>~@#T?~-A}eoH2DyFG~QgMut!y` zsLJi{sH*Hm5Uti=L@hdBK|fG#b+7ZUpienohN|(|L3eRMWArvsKvJzTPiW2X4SQ;& zr0Skrqdj7Oi$-o{iW>ZXDV29-TF%|2%s0HHSH&wj_hPyb{G)$>`JM9ZG0}4tXU9h* zKcEJXn~gRXju{jqE}FZ+VvWT$26YE=iD`ZdJzO-*Mk~!Gr}~_1jAf2puM{{zM>B58 zr`7&deV$2bY_efDQvTK(vsqBSMGuDZCjagtR|v9|ThD*7b}}KhwMnt9YU(Ybxu!#m z-$;nDg&65D25YEhYv|O?N2Hd&3p&IYkQk5A7@Z!M67)lCXxPbR9$u}OvgZ&v3Z>!B zlyJJ1dd;i2op_q9MX8rL?q#}rDIIiMC;|7fzkAu+z3l5=`rON2?qxe_W+Rh9w95z_zx{DO^`Vwn)fUG1;27`V7(JX8V14z1-v`(db@oa4u;~M4W>*V~*;S zjt+&;Bp9c(+T$3zqjekQ4U=j@)3*jgQ&%goLL)I!8RTyJV39B(8nnGk*qvuW*!J|y zR!PUiP;X-B@C{D#XRTqnS1h&4BQ`Na1cqOd>}VDzI&{kH7N=4M2I}FXr`5LoxSmMz>d2{rG>s3rGMohZ|>ZNNTT@3G@ z;1mC#Z)$k6Ch^Uuzn3=+8mB$KAc69K^nKE9VbNZSMTeI4W_Hl}Q0iB1WYIEhzs~H) zH%-WYzyueSPAd5?nJmxeO_{kAN1N5!9rn_hmXgn#^zhDX+kNW2a@@lhhJTqc%!}GM z+P56-{C(9wsYWm@RVaxIU5T3@VhyXC>{T0YS*c%>HDP~ z8EsX?t2bfmZP-q}L;B2`q$Z%c&_-~s#MR*c)M#}vyg6mIx5G~CEhhtWWwdW@EXfG}25- zsjs1|GnA@9meOcbN=khV<#a>YUzoO(dfj<<9t zAC*v!3N0nK`Wnjnl+xQ>{X*Nh^h2FbBFa#TT=Yky==gRTo%9&jh?$;k;@aR4&`VB9 zut5mUG6bJ}jPcPy@@W*=Iv48;T@|tW*zV;x>4XZ<82#fLZnyP@JsU-Z>z-VOgmm0n zETS@p8n3C1$W4QPc!7kWC*0sqHA=?GP`5oTjja^dSiWFdi*jwD`(U*X}6uHEqSo%Hg7O9 zcG&S_(Hc+Lr`3MNB+}IA2~9zr#%yy^$o)3n+dU!cv*#FNkYv22KjvD2qod2egyr6$ zmRmNtnJMPFyg4GiuY2Na&d##AZ88&6wx8zYHfO#oxxBQ;=>3_f>MOHNcUA}fSuaSN zAdakrtm+~nhk(U?b$J18pMy=`gavjqaEFOd(OX= z;M>ZeIe!*X=s zuiZ-f8IL&s>nbhky>XYr9)=CwA93FOg3SN)s_t*e2T4BrO8p!5Y!M}C@aN{M^y&Ed zuP~PzC>tFhvgUK6gptbF96blEj+zdJ3}~EHea=8A*h+k2W~^&Je#F5J`@G4b<0WaN zUoQ;&X}&TbebPLVh!0)Mq$>a?ehF58;_8C}{#pUEfdAodYZYooZ`^TqTJ0&gu%tfZ zu!Kb_B2h5b;?c2}nkZ%fkE;XEOvO#UPveo=?v3YlRel+ym;pKQFr*b$PB!tJ;<6Hy zFKo-OKQW5+CW=;~P@6y$y$maVJ|Hpq-vhj?Y~QHy>`ID9m-gzWEC#gS(I8l^1a8w! zQY|EOkllk1u$ z*S-2PDb(D$*P;(@(sZ^Yq@%~(Z;_ll{X=GJ+44q|;$!fSKB9+cX(P*|MEibs4NxcU zIpf1R`A$hLf#W}F4)Hw@+rDpgc>TP2tz!G1j<3IOUQ;m5(I+~-ZZNNR$?JQPUsqSd zN~^rOvBRsmyh1mdqYFE{3i7I5Ugajg+I@cG?*5It&u-j(X5;QN8h4-8xclTr4s_Z5 z#m0^%I(q+ovQveTbh2?oKHK3N!SR)Xn*GlE|9eJp^|V7Wie~%Ll)0)B;$EI)E_b0Y zEt27Gwl6VhBRX}wL$%G{Yv#7Kzt~!mQ|I4#IC{73mkS$;d#3-6@vr|yc|g2Bkeb_I z-XqU%n8BE9i5jD)Y@{46q#oFE_`fIT)21Gxobza86DH*RSI32Zg1Jl~^wW&da33p*~BNsV_kXQfcT!ZfljQ|=XMs|<2#b0qnpZP zxKov**Oa0iO)v0Yh;^yaYO%Ivf+PC=JyiQQrP|vEvqTd1n&D$CimaTMY=;Wet=!LA z0xZAmXOb4c-;B46J#KaW$GG4b4Tk^cTt>G+N&n*&n(Ksl=aa68SHn}yWr}LJ9!8@T z{72&{y}_0fjaH9wsOtT%Y5%A>da-l_ydrIk=G;ZrUX{H5;@P6XX=kj47=1Wng3tF+ z+Ufgp9-nQ0atipX(VTuQIRlz=whgzxoVtBEPrrXyPQPbz`ZeeD*oTXclVA1ds>jpp zPiP+RVShPkKYE4`S ziT{oNJvx3b?^!_BG{8r)Q*RrjJM;nVywCYH0eQ;Xy(unLq`tH8airN z`mmwHh7B7&EMr*auo1&X4$B%gYIyqap~HvKsmd6hIef(Mk;AiwkIG2T7@9FGV|YeJ zMrOu{jFA~x8KW}OGlyml%N(AWk(rq}B6DPBR_3S?=_7`Y7&c<~h>Q`LBSwrEIU;Ms zsFCR-hmIUJa`?!Mk(nb$j2t;KYvib`^sJ#-!?K2FWn^V$jmR3Am6bJW6fBM+`B6kY z3bIi=qM#<`NAg*7VSZi9rAUIRWXFcXp{htpMfrqKQK%r&b$Kd9Reni0WXMP=RXUNv zA^A?!MrVrhOG-n9hH!FOWt>yxH`{RS6ThPTvilo!YG?MqfO+7&A86k@_XCpdznXNv z(%zqCF%{oN-wP{BOY^G?--aMnc=B0URk9>M5*kug5-#u(77z&{MYgjlW=5(?%9+7U zDKAY|<=b_7hQE?Xs472Fk-(wNSQ7L#_t^#c<>eKTKsXc$6j2U=%8GD_u$C0O!|j(7 z>8nCyB(x-yG7|Ce^2-iYRh3s<5QvvwV7@+&m#@9`ooXIC{4K@MgaToeRv=QL$(RuK z|6>!wS5$_|NyX=@S`hLrmA~`(uS)*Q=g+q=6q!hNbMngzOSw%gf66huyu6^KLd&46 zqA)Z$JfWn@CkglpN-M%4U#PrrdXdED(<%_0Jl!{Wda$ahLOc?w^usMzL@&g76=8=f zxhyCS6)YkW-s%@*nplM7zwPUr<3R;emljaVStwQ69-h=v9G|a3EAx8DamA=O=D* zewNTgtAl+{w&y9-$9KF*Ci|+ZsxXPPOof4>s*17;obb$M$uE2ET<3{-;QW7@xyaUf zd;!{5tfrzc5L#9csuT-p^vntG@Ef?wJaO(*MDMfZ+(PW94`z7h-)dGw7{Uk z3+QEyr`}DFWO5~yF{4Y$BNq{|rdL|JoLbIb1y#JFq>yAol~Q4=tHR_r3@>#0@(Z4vpEv-`H_8U~ue$@`|c55`fRL zlJa~h!Y=qb#pQ2eUJ&wlPd7k5E2_F6RmxLc`pLPuZuV$5Xra4KTS!N{^y6mFbm>1Q z@%T=1>&nUYeMvbr99^kca4mfr zv6>icQ5W+*{=Z`{&b3d(ugTBaPiw!8{nd)))StnFO+cG+wzcQ!&U5qDwMS>#2M+z! z`Q}0VenHZGB$=IJEO>urzsBmIcD3r@%A1%zxP-Idl;-lp(^+~#urO)Ln3>A#8 zuBw_ED)Uu@CxkSKJl?X^j$hY$T@iLUDk7zv>V+Z0(m&_7u${fIuOvKH+no}+xAeiu zH$^BJcs6K6l=ba7jYH_*_pX^tv3WY^K;n=6+m+tfmg;tlAO1!e7 zk^-+<^i%|HN@nG6iMn-Id5RxBS|Ug$iewOm;4D-SK58Cu+(o+}%~ zGPzuQT*kX)V(rZ5mEh{>T28K?XF9fAMpf3$6ULs7D`k}DlPjNfRgAAFWiTsBL7{3v zI4`6i!{hR)KSrIqkyE@qvg6Z)XiyZddQhoCWeXf-nIWB7%Onz+7Fs%e!L>fMB=#vo zda);rE`v@Ny*v31!6a2iRLUh`;*&Be&0juV#@=QyZ{zk=$;hy}(ie^!zp+w|(~F9z zLXKIOi8&}J_LZusC}Lzev$`ZA1Lbkl)J0U;SyjumN}5_rU)ytsj^>2&D;ejLbZBuk ziZeZ|6)^0nvYnfnVd6P(>Y!YEY;-|I8Lo;jW@tQlpz?*)T+dLGrDZC4L~ex$fG%9v zx4>3>Zw`smwXxK3W!z`#b+~!~O71h{cslV9cAIhP|E z_^Rd<{%ZM?k9vN}--vk!V%N<-^uNWO(9I}d z55?~pre9)e}7E-TkWrd%*ErrTZe!P>PB7Qp$6fZw( z*PUKiH|cbkKk|vy^hGLiE0%_;#)b<@N~HIqZgMB17M<~=9Ul6M^TX3Z%Tmr$s2mv~ z6pIr;vsW&m>CNJVECwf3MEGYeq`xptodt9r@*pD3F7vMuCNXGkvpaptL&rZgwy;qB z?Kmb%%8SG^Zhp@A$r*o7pK1IK_BD-(dZwm^7n&!j_``oqS42D(rub8ZbN$g*&6OCt z3^#;QD#1Nt1-W$kOl8LBAjRJ`ee%2^b?l#-$8+WZyW`jCpP3H0<<=2prx-L(vs2~6 zGrv<2#>|xAO(GZlM~+~KJ)9x7C|jW4n82B5&JRdj(kd@6E2ViCfBYq)=khzm_^BYJ zq2Mxl(P4VD%cnBzm^jn4yc%7~_}uah8!l`lhN|QLIp)!4%fI9`tTVa&v$8S=28M}` zVL^3~biM-@jR~Y15p+z&m+d@r>hlo$fN2l?rIL@EACau>_{qeK{iUKyC^wgHpV9i% zD-vUn>z)QiZ2^K{WG1vvv!os6fk^>gskeM<}V>% zwunvrGy>@|U9HEVWtF(NorJ90DpmQbJ&lx<85_`_`SA3C0a5$H$Y82Mq_{9&+is}= zl)h=QOBnmCz&|WwVxe`)sxwq0gnuXn-^uBxtJ-CKehkbpOnt`mGqR}ml6@!dfze|E z=;eg`<>ATYQ$-+5fARmoz;Xr&13K0RNB^Dqp7{I-vrjUmlpipDR*{{s=C}OiP9Hxu z*IXz3f2id*4A!!(@0slxl z$MM$reAbWe_``iy*<3oaaU1OYV-{bx__4(kEF-d2MSjyA#>{Oze#4Z{ccp!9-LSqp z?ERw_cUt_&;_)`Vbc=GHvi{aJpROqDY?^uxb$Uc8^f|?PnoirrnGq%s3T!f6$9Jgh zW2foKdYbscp03kN_P-FHP5|3=J)T>iQ&YOmM`Aq}Q$InhA;ZnC>5M$pu9+!`h|f&_ zh&yPII+G?;n~504z!hb|%8G(wYAYSUTt?wnP)SW<@%rmC%T_MKh7}4A^itzQrlwCX zx;zwGWXYx%X&mG@!FeMi7onad=5Iwsd8kmX#A&EcO3JU~nRtifR^F4$*urbEpUWiT zTv>=^oDSkr!o7-TA-`0Jk|QdKXp9}-6P^1;BGo%4dE|P$cq&A^w;B1SvVY-(_C`7- z+Sv4Gb(FCq1^HEl{R=88mrpFsUuYYZzF3U&av~rP1KZA+I zgx5ZoeiFW)$_@z}V}fTFRqFc=2_(^XNWi6^&WuY{I5`2ejkJG7mF9hN0?G6p60rO^ zxhfFn4~}@;yud+H4EML-9{zBzTmc$o@rILf2FW4hOEdx5a=FTZ47ync1Q*e*umwg6% zN;wvk6*7D=ZivY;h2!;SbeV4%FNT(RMxmdr{9S4Ie8-uaY*y7(6buQ6N{evbdaAUu zz9(<&EIV{jg6w!42zqQ~W$D;z)3T@FSKFHkPsce$I8XaxBhCp`<7kRE%qi$J{se!_ zm_jo}K+BWtffn9V#1ro?#Mi-&t6-47w1WrSa3`uy%Z0AcwDGEDx_7azUh|4@0e+eV4x2yp)zZ86U^v78%SQJ(p zuTd0*C(6n_iAsu&WTjB(geCVUc&?1eBwL(AN%>#(FVf9fSWdcTb!y7;cjQ?lgJCn1 z@>%n3Na#;hd#bS^=6`2>Pzw2Jx}~9I3-ZHikophJU;sLb5g2X<{G)-3x^Oy3A{U#K zvOjzLLrwf8<+KPgtYvAz{D6t2lmDk4V*HsICi>jLVZ6?0RB7P+z)%@R<_;!HgQ>p> zsU50(G8v$Xcp??!X|%g`$_VS(hsgiPEbI3Q2S%r0*~Do74<2ItL&d=$8BlRt#r*1A zS-x`a)m7&v$i+!y{2|6aYNUyth>g&lV(2t>pYM?QA7=F@ng4Rbzq6hxg+6-hMNvs< z>A315=IkVkDZ(e$k5u7oJL2`{h8LAo%B;k=3KnL_Drfz5m5ymMtUVR(sFb* z_0{8N*o)kWI>x9domMR~yAm;aJzKD=`0?!*TW2#%7a}-7al_@8kI3kTj)mC-Gkld+ zo)*?*$JPAeEk@MtzsC@YFc_g_sl)osK zn_;r5a%L!07#`NiGntPlo|s=%rWcaIq~GP{zDxUKXUADR6&@n-dECsyt+_ZlGFW@| ztach5hzu^I{ed}j_H*$!*u6NyYKro)`g5r5cDS@6!hB7rQ0E4_h+p+MwH$OHKfQ{T z;f#M}racv(9pce>`R@C9ozuk!LnUFFXLhgnLW@mXX%;VaD=%tHS$;X1tV1f9iehPi ztev6T;VY{SM`nbiz~zZuYt*IujsLcsttktgwlqa}&Cj*WhboCl@Df?znKK?2xNTIV zJbY*A|K0tU?F8syF0!2{tOqZ9&>$y7N9@rWJDdXy?%?l8HqIyfocJ!VWX`>_0@b;9 z{DIE>TpQkRF#D?D^oh;@g3%3)~3x&_`P@I&p2&+6eyn|- zZo@jy9ojG0d^8fDyxa3}VmynJ?j_8>JAEllGrBX4z^sZzT9B#A(e+>Xe5dq`#g6M` z{z`ke*Uqzl6@TuDKUG}k9bwXa5p;T~E4~nm-NxU8{QZo-m-!=~Iz9)->yjLwx}nH> zmwK!1B(1b(TpM@P5S206YOr-3iO=`b(_^urCv&nie{wBe-OQ{*lNaRrmz| z^1n2nZ*re19asP3dY~ooudg2?8a2#ar=O6|!jkw7-xU0+{wCOcTew|?N9u9EnB8dN z=$EQlxN8-gLbq}n79KZd$3BwNADc3*y0jEt;`ie!XiA@7zoGn1*{SQB)`P@-MJnPHdgbw3 zI3;1w>2H?hFJr*U-Xr_^b15g{>XIMU9mf6U-gRe(T)qiXL)N7G?_W>KE)H26&)U@P z@@YDJrou#Yy`$NA!vu6hCZHEk_d+c5=cnUgiSOUUzsCO=?3`<7jV5$gj$O_>bSIy< z9{A({-k7w(GQO0y*RKD_?mGSWx>w0r|6$_9{Y?AoLW}M8+<$I!9sD`_6V+!P#&87=mK9cRg%$d2?eeiE)%vnD?Juf(oMCZxElX*zOuoeG5JARdP;m`{$ ztcr{^4kws1f2i-t**xu5&!Y=L>x^N_HvvWd1y#!{BNhMQBIsk&56+k|Pb5EY+~nyl zcky*)*^Ycz@;#4L{`@Vh(5>F#c`O_%@{ONyMc%CG^JLpkMS({GDfE0Pn*?NZJ4D7L zqIlQ^jB9a8cxpvqHU3HJrSyQ*SIK+(G}lPjXfKZ$XnR9(JFA%4-LS7iaioyH)Jq$8 zrxKVBdqUbV+qQrwSox*Mr`G@Skk0t^&lJ0t$=uA$aH-A7WYn#{v&u)JBCN*v8R9c3 zoBy+&8)y6@hDn||_u7te@A$*yz4OeW71Sk$)JKX(A3Oa;?&(dt{Y&n3qTK0UmU5^K zE573XIK5AtO4sA32<e+`kYas?Grmt1Pr|s*GrnaeOKUr0 zP6d0lUHx=a-_cJtD$@G8^-s?&3Rh$F5`WHmJF}b}TWicgJdjf#IBY0V9iK&U+#Eh% zoV9t{lblx=DpK0dy}!zC?*is8!$Z(&j_$GoaZ|NA_PM9qao<>6Hge${dG4Ha&QV8& zOwe{&zfBm!a7WEG(RZys&N#`jCS9lJwI^N2TTooE$kga?!tOcZ|D4Akj!F2u<8}7= zdB-(9PCfj>`F(x9FVNWR`SSkQX0G=K_Q!UD$MoADyAJ$ca1R(cbARj@CPCJL1Hm3& z*&oXVUj|FT_rbMbzq5!3Tm`-)csBIluydg2keVz^eyA58etM!z9ov;6U*1 z!O(**gQZ~F5a_|Tz|G)s>Cl7I!9CzS54>w}NZISHR8S-NT^= zUk3MpX&KP7!u~CAAb4CR^x$-`6#Ox`7TgVP22UOVJ$N&?2Yh=Z^lUEZl?6TcH82-E z2$q7UkAfb&-~#ButH77QHgFGEdLi@y;s*zUUl@C%6YZc_Q>@`h26nf#AbnE|@k6 zdN3Va3;L(*k8J^O09(MbF4-Sz2gl~_kM;hF&-V$K4&E_!e=HAt6|4kfU?aGC8uVcK zrO<=#fbHO=GoU{U{hSFs_{3c3!E>&G9$W)9f>+PmAKMD1y9iQvazIoRiHpKlYG z0lopw0-Q_Cg{N*gN@)HZ-yQ`eGT;BC15)^_!j6{WPClC z4nABDJ$Tw$=)pgMjo?okpa(bK3O!hS8}wi;*n6PQH?I+T@RZx32m7ys9=r@}1m6R< zfZtjVJ=hGkgKvYq2l;#lz;rP8o6v*zf|cOPJD>;KzXd&b!JW{9cY*ETqhN0)PriN^ z^x!RbLl3@q5A@(A_d*W_9)KQv=t1bgx50L>;5*RM`Ks9jJ@`E^4~%^udhqEVKo9=z zA?U$bo1q7P0Jekw0DGrXjz5GR{Nj(G2ZwBd9-Iy~f@k~$dT``c=)s>n20eK9PoW=* zoVGy^j@%AC_yJf6&U_kr@Y-jf2iHCeJ-F#P=)peELqCkK_JHZ&9nH{#Lw^Z9IN(L- z!Qe~KgF9Y^9z5(7=)qxN@8PsRU^;kG3-sVBuo7JSI`rTj;1=+nKR^!-YlR+s9qgTf z9r`2m;4&}|{0OWBH@*!$*uM>WaPDsC!8^cq@Yi7PO!R6G^k6HP2M+ry^x!?imX_!3z3zt~UE|99-? zNZL087Cifos9`Q=kWPPlFyT=3A|Mz%}48 z7y5k9fdj$UPlq1--5Jn>Up*6g@ac1)2jA!qJs2JUJ@}3Dpdan?4dP231HnQt7yK<) z3SK+tK&%nGY4CyA7Vx`Z3;4m%1F?2+@UR21-eYLrhaZTggAZmLh~xSsP#)`2JILk|{#JHd_M z$KcCgpK;iU1<->(4M7k74UB*%6+sWK2e*Qcf;+)M3!w+6fqlkfpG%+^<0oP6F3~m(@TIJ`3&yH{Jj} zc-W252RY{u%mA+iXMyj75wQ0v=)pT`55%^CJ#RV?+XbF~^MTl2a3R=tqR%&e&4Jix z@EP!G@VGk8jRAw;-C!B`1o#m67WfjlAH0Omjh{Bj=UW7h1}oP>53X*29^7`@f!Idy z*~SC0=fH8dABepJE&zMxus;3k2V!S|Uj-+CcY}rC&(|M_tpRWOCiMpV1bhkHc?b0d z9I=6VGa0$uO}zm(-AlazPufVm0jGj%!D?_b_>&)?x8SmekQ?|I*k_8*_a>MDUbY#z zflI&$c;&+fVjI96TgW#!>Br<7TnO$5cY>!~LcilDpg@19-n+l2QzyejLibKf~;xw?F83>`9~d$Z3S-x zcY+7NkHKFbe=yc(Cg+Zva4?nu4&vLqv%qhi1U=XSt^@aiTfxmILl3?Sehh|Ag?<+8 z?P<`1r+gWDaLeh?gMS0pfoW$z58evy1Rn-J2Jh&1FxGcA;lM0#`bY&EzVd=Py0BIv=Qi=hY8vZ22MeFo10i^f3@P8|vp^Oz6SES3wUpUJX5X*Iekq*TDzD zv}>RTZw2209|n8QrQXeh9&7+7fDeO(;7!*;4_;jgJ$Ob1^x&hF&Ue+}cx2=w5e zzzN`jYUsfm!PVgF;Dcb=66nD(;5*Iu`_O~u`~Z6J3h+7b zUho}o^Fz=Vpl_R@2bXQ(o7~`EevI7_+=|@+^L~!q0pHn<-2wYO$2vuDBX~?9_8c4t z4toAzEEk;h!ogT6IH&nwY%LfDH-oL6N3$}u# z;Pr!`2L}#@9vlb01P&eoJ$M&*Oey7(9*d=ecY}H0h~crAe5<1wYy|sc#9~{)Z-6_& zN5GH4KxQn~r;PFOh*&HGylP}DHVe$+bC40R7F-A31#Sgw4!8*neU5A0ixy%eO8ZgBMESgZ;BKG+K0J%u%!mH2BeVI3#<zz4w? z_#8NUS}gVscpKPrG2=DxEO6ZPSZo4V02YEPu876ffZM=L;9XZDcd*~wSgaj<;~L~v zMSSy+8(0m_0v`os?dnJ1I>At_#U_ueDzxB!F7wEkB}ai0cMm!56%K3U|kvXV6SrM!T#V*a5?xf_&;EuYU*bN z^x#Zz7MNcNJ-7i}2R4CQ!F}LPu>WG{!5@NsmQa3R26$sF^x$7^f*!o%X6V8F;8rkm z4fNmxw?Geu*FwLP_`wWtRRi?kd3Qh${^3sO!ISQS9=r(L3C;sQ27eFsSw?w-8Q^d} zAw3J62}ZyOxDNaX+zNh?uU+p1e-3^Oz6JJKj$YmmJ@{2{7TEs*=)s%513h>jxE1^f zxD(tCehj_>_E~{F`!4k0-QX;+kI(#iteD~Jv8;EGQ;#{aS7VPOd&~3F_*>I!f9y;f zMxgWX^pAKa;j{esQ4Z(gxZV?w>pLaw3rl;|_%1%>!t;iorO^oe1pWfV+Y5AlLQC`R z>v7cn*w^IY*yDQNblCV~k2q`v9GSSUSyJ|}9w8TcjOQ~i8Nl<(OF>n`w^CVlx|>e5@F#R=jtutmi=z40fj=L7Pkej!6T7xiik^Tr06*uCt&|`9 zm*8#XYX<)-_`#O^dnP!}aUJdUGr(^FZ?m5ReiwL~{YBt^YsG#Y_(zYj+us0wJ$Re_ z-QYg}Z)+dT;Fl%Y?e|2xzYDz0eg^m#z}xKSfS=XLZhsN@1>kM=>%iX)-e!LT_>o83 z?e7LZ4ZO{MGx&wzZT5S%#dvg#-F^o6v%%Z!=YYQiyv_b1@a;O=?bm_t0p4bR1Nd{m z+wAWK|79!oo5BCuiv6C4q5U6gx1Rxi3wUe$2fquv&HqK*tH2d(W91Lj zODOmJ+W>wV(&q+vS>U+k2Y)yCUVfhIpAfKS@O9wd^~Z1IlElO8CeGo(ZIX(o0_t{w47B0sd}} zZvcP(pX_b03H(&>wlPe-$ZaNgTN@04-wOUKe~c18^V(P@_}kBFj^LESS4Xow|H9z+ zfiDU0(KT!h`1HQGb{XK`^wQUZ-wXb3lq1^4Z*Q*zs*W4L7vNr$%LC~Pz4T4s*MQFp z@G=L46xIRP0Ksqf^TU=TCj84a;pWjBn)3K2-{S9bzz+_z|BPt+2fuSzbL8=W{YO3f1>k4k{;R40 zU*_>k!LJA3H892;?Ts<3z&|Y=Se-HTQMm0xf1o&K+?*^=G64Hae z-OuB1RrV0L`J0RFCV&5>XI=@WkR{A&VVH6C+ZfN%78^`iaLFKCX;4%jdD?1#WF$Zn2Q1k%eU zfr?Ki_^sd{4y3=|OCJV5VFK;Z`)_#u)q~#${=ESI zj>k8Ee_#skEyKB7)cysD+I|!G_b#!IP0rD{had;{*aZAP!}C7`{`5ZYzbI^Pe@)<5 zfqy+VN<3D{rm*$;vL z^Q`8`Qvv>QkIw}EGWfoJ-gk}|2LCqr3(ymKtBVS}#>jJ>7a*c$>rsJJ;YI*@*tmp&7G9{Ahem-G`XTKi&w36n?F9H4sk8c28Rn;8%YoPyL=k?!A@c)@(U;Bl@e+B;PK>pEl%o^}x z=AwTD+HW_n{nmpYS=}5t&7VHcb>H=`0sIBvrv&_)==s+K{`v*Ykxl;e2`@&=-xcHA zLi-+T2>dCF?0XQI;1_|vC}96W&wd#EQ{Zjww+8$t;H~u!{@yDw{szi7$SYq1_!q9S z``-lq*~RvAOa}`9#Wx1*zvmo!Hf2Kc!iuby{Ue=W|R z0`d9Gi%$*srMMS)eSm-3_?zXAODe`}8172yBo@lD{Lzr#L$I2f32xzp}{2>gvJv4<6~f0buH z6a4Y&{!QQ)g14QUs7h$J2IofD3yqHdOAc2C z-0>g(l&?ek4Ag&ewEn?g`%EkK5B@&z8Gip{`$*~^{0rcB`uT+Ip8xgWtDeO(6nv;n1bKYQI+hlwO)$p0=c|3%={^9-c{ zK6?G24*dBqHAngd+FoC;?QH-*;boLRFgIM~%?-Q3KmA&BC%q7~tD^d>!~p z-a^|C@PGFB4dBbRHAfx@l1FHDwZ91b7Vx%xnL6;vJDMY70`VX2#eW0%OLjF! zUh~^ec-ixBH~2Om*!^n;-vxY4AboUg+_M+1)Ui-@cub%6%T|dhKUxnvM zH%E>Qq)+nFXMj)mpM7662mE62p@4tKdHyX@{5M#;1^B;td>#0M-`dZuHh@psAN$&a ziurEvGr-sSbw}#gTLb^`~F=6__06R_ZOPLj{yIvKL!aOdVNT}%k0Qsu)i4Kzw`JI z_>aNc;*$yfgM)aMGm!pfFMSyN1;5(I=o;|vfVZ`gdd2^S{fR()O1=0rfbZR6ZzE0M z&jEi^pbh`UYr_sUUROu#b94xNAEzbqr$GLvc=>07Pi=!|QUd(-9v=pOZ$gXp+E5Mn zr@>zph@V_URdY~1_+t|BOhv$c^d6`N@JHcUyHtOA-oq6F)&#y6cx;14$FQ-|{uQM1 zXp7B-j?k=AfF5TOp783hH~0s@*97?JJdp)H;jos-;s77L&#eG_F8Gk2_l<)~$&bbL zSTFy?5l1Qqlz*$h-`Ek)kF@pk^Sp6tGx)tn+w zBc3bl<@cZ0k(Ga0;NJnS?zf5h|Gwv60r(qxv{>iCrQmmg-xjFjH@r5q3j8uWpJ78qlvdt>Zo@Ryuy z?_+zwSAa(tqV^{?%paE$}Jt^M`}zY~1OpWfGgv&hf)^9jD|f8bMk+uP4l@TuTe z9_I5eafP>zUj_ajr{lT4fc@wm=w|Rs{?uaKL)ZiURq*!&+Si?4`)Ydz?!P|MUVpv8 z=YY4>Ul#bw!Q1Mu0Q}qFcLe)1*i%)Oxcj38NTmQ=f{{{FD{q}u(*ahHM zW!URyDfrL9+xpKc@XopR_-_W^5xgz_d%#zMk2QX&n6y0$>s0X9`^P_ZAIt)Gh;Zwm z4>a$eZ;!!9@VzrzBBupnc#;>xS>VqYjAsJ`Rggs@y7A3;5!V%^RIzAILfVq5c1y#-WiVPV*Tk=J)b7^ ztTah~#(yJQB7X?jINY@X+O3EF(EJRItsIlUUo^^Ijw6Osz6VW-Uy~5MFCzo| z$#}lq*6(w`H-XO!q?Zi<)gBgsKj{L*DX?xh&RaLsDIU+sPw>}Q;yACqH-Misxg}B> zsH-Bc&+P{P0G_M2wfAQ5&x4QE-b3)OX9})ufOUyk8 zq;lI!0W^zp?e)17{9WK<)u)QnD)4uMx3#y;;C}@lQWD+qYO&r+xDWj3q8975=nnm`UkBdi-d;QbE)|Bmqddkp;0s+P!=0e-&6Zv|gJ zw0&IG@trX_N7J3oIzJ5@lp z{NTS|))Gkz02VT!qHp;di8wq)C9F&c9;NJw_&%bU^HcoYy4z7(I(Ck{%61g~F zLr$7hEPess=?TOlz~AWQ(JdWq9sK3~SYdsX7#2T6NS6W48*7dFQfsI^t{K;lFf_N} zohqLO@`>(0)_|YR90RNQdM?Ls`;5!BCq=PKvkVchdGc-H#J{H^hU=R2~&)DbX zwm9)p{EY!0mbpGC`@O+G1iqJ__l>(*;2#FR(9ip>K^K5;gZIF^cLW69xblv7ooOlf zXYu|P+j+$*@b7}R&HJ0dzw;8_j}wT|OmE!S1OE6|?BjadbMX8!_*m;exBTEUz_0VS z`@|=_aU%S3r;P-;B@wcVKgz{aHYMuirn_ zUw23Q{4!|fZMFB;N5QWF-{{XHL7tOP`ECJ!*;_4<;r=`l2TKOvd*Z)+(9~?RuTMIh zi*-17+xp~8@Q;GGtxv{+|Kr>CI2MEN2;LUQ<={)etMYjL#J5lL82RG^^Gi2xe%T8C z@^|g^ybt^`@V5LrpaXsZK34t;JCl61zaPjsLW$}OD#qE+WbClVpd9?U;O`5J1$TL4 zK`r%jNj6MHX7 z#b`VD(co<{IspEC@V5TmITJAee|{haXM5vGfABwl)?$6;YZCYcpSMI-!xyjo39Gz1 zr~;q(1>S!Z;GgyQ72xj!e|I4N+r9kPf$y=mCGtfe{ij~~?chHF|4e{?(&G<+pZAq9 zwffk#7U(zw*-e2Vc7n*Q^5mReR|tf&b=z*v}56U*V;%0zdPcmdJI1 z^jCZ7SAc)(+ZOpzI6Yq`{OYA&2mZ1B_Vd*3;17b|6v+QYFZ}`VcjNtJ^8&nV1giGa zc?j0I;BD=*Klt;%$2#1fUQScpadr~;A>beI^S<-YD)5`ZKhy>SUB1NnX!*g9!h6zs z`P2K>8|%Q22EW+P``#gRpWjC3A~wcn4e)IazMKiTJzN#KjY z+vbrf@C(4(=8+ZPKL&qt;5_RD?>uWA_)ibvJUkG;OTGAQ2j3g-i;J~~sp|It_@nS1 zIV_8!@%LS?>Wszv>84}cH(?fc?iMZUt%C&)!x<=+bM2aw*@{?>sX zoN&n6KDL7&0)B^o-!Ab#-aVTK!2jCeP~^Qp{NC~6*LfJ`nU04duOz@3uYGRx)_eWI zPsIE3ZV#l7uD2$Ef8%JpS1yqLH_v_*_|=^cS)Vyt0shxx4_WWqTL=D*F826u2mcfJ zkUze@_#YrY(a$H0_r{0L!%_cT?d_vK_@Ur!?RygV3&Dp2_Qe5Jzg6IG0dMQyE5P3h z{@p;74>r{Ra5x^|bBaBjC3N_}9Jk2f+UW?=1`m_^5xKN8r9j@UI8>O-RW2m07=UK{=e{4~7Naz>z!<+^=L`OpoUeDxROou&Tt z%7^P^>qWKm3}}+@zRPnD^ZA)L$h&VL2mD!KdplnQemQuRpVv=(?Yxe>t(|WGzZ2;L zduhJ&gx%nqa`Apge~c0$UOhE~-<)^IdOe{hE+oGM-j;s`_zwAqalrm>p8Xu~`@lcp zFJHpnJ$@1Rg$0KqfAaH%F739nI`G$m@8#!x>%a}*uLEzpKYBO#y9y6QZVrrNH+XHJ z8T^s64q2~f^c;t0)4*HH5B^T@ANu|C-S?0Kesa;F$eV%uU-R-`1l}pO&$D&ldw{pi zvm3zo0e@{E|LA=~yTRWG{*QtD+j;pngI`dJ_7mVw^7x+Pu~&=ttnO-$47_>lU2h$g z0sfqYhawNR^Yiz6>(m_ZFE7FSLj(L0k6#4-^yP;lPx{+W;$vPPt^>dR#zT>50e-T_ zZvcPbCVTtc4gOp3*!GV4@4Mfj8T{cl+sE;q7ht~z{7e4y3G2P~n*siD@VSBb<#_SS z0sqY{hpg`A_E&rMH-HcQ?T~e^WHm{ws|54{3P(Ud14XxYVfx6h&u2)z=!tWJl+$VP@jRfjXxRSKL`Jc-@k;7Uj5~O{|nyFdt#vd z%7qR!e=GvO>=AqY)q#H=eB@Y&z5e&>u_^H~62yhoJS+`{LJ3zQoV_ z?(gV10rz&|eZRJKR0jCsmkwFqzm@~OdjtA+!2V-id=`QKJNT;u{34I91OJDY?eX6L zz61E0K>CGV`rY7*!QUI;S9*Li_^-gP3Gk~uz9&wQ=5IO_>Eh?*(Ox(H8Q@F7@ALBs zUwG+rz@Pm}?D->vG>gDr5B?p0dUgFsPF37I>Y?es8SixWYkbd7Hh|9pZ+-p~{6@Ts z`4)frgypV}A%#uCy+p4cig|CVqE$QV4UKw_v#pF9T)xuUCdG;JaE&TQLU*zO{nlYoyk7OFD#0e?66^Ud{eYIHq37Mf@9{%xBN#o&8vKNMLTD8oOz zGAsxGcks6KkAd&_o}J$czT$m*JKYEV9q_6=W;<0_hl_;|?ER}X|9Ur!ZeK`-rfO&G zwxIe-Hu#sp|A4=}JbdTA<=|KBvX7ay;0J#YyRC;{axM6x-Lc!U%2N5T9hzIAvGvyj z;J*VOQVC-8*UnRf&-3%XXR`Z)@Au&$>pKr7fiD7Y+h42#zXW_=zkT0*Kr6uC0^YW+ zT?hUr@Ui+*2sXEa{~3H&|GHJxV|#b$>c;37XukR)_S#0-=r$Gis(g7U@?ZSz#Ym0K zJKg5$C`|@5uS0X4e|@I%sdkrWt|ko4>0cj;JRRtlYo$TKhN`cn(5&BQudh|$yZ_H# zUz^Ei!j|W&OoOidJ>b)k{yabLdp@G=G~9OtK2|*4@gMvY@Xz|wC&)btiq8W7E%lj`9oHNDj2{n0UJcl9@a$)S{{wy>;Hv=tna3A^e;2&X z{!;Msf40Ye75HlKw*I^s{0{ITfBwGtcMtgif$}Fu%b$a5s=wIn_Xa=qpxu5Jc`PTR z?az13wgCKqP z6!%wvk2Ux8M4H~<+x})B&$7Uu0Nys96@Whzylp&N3jP-Gw*I#Y{H@^E9pj5{;uFWH z2B!Sm4F1%kB9U2v`px(1cMtf|q=@x;aNEo9oG|!8e|!?6*UWo^&&2Obr2Bc_{ViGG zr-BdMJDgY)?f>8(IVNI#$IMdj7atq3uFqD1UkcvVe>Q_ZtxF{G$dUf|JaD9r&mQn= zPK-pJPW1C@-Gz&aU)$-po^f)-+CF-Np9bF6KC-~)fw$FP0r-6Ii~aHQU9VgUeiQhR zpZE3ORpbZw_d^qt-OW(9{=vV0nmzp<@;3k4hB1C1{bGOqKL2`yABf)%3Hf=Se_7-o z_45f2M#n$!FCo1x|E1s?z}xa)1^&d-?fGve|G0yXUe6>v>gB%&{9{OeSzvye>b1|d zxmXL}cTkS=r}y1g-5dO8;5+$wxh2Kze_7z)1K+{Vk8rbh`2z5pT1meY{6E2G`qTU7 z?^WRc1O6&MpCA)>60ptSGtWf%6#xG}|33o%4eOvzB#C@k}vy}gM9*16)<0E*S&0}5OFpqV4 z%6aULO-NJ2<3BR}S|0Pz``SW$E5950{Vu+&}6SWRW>)5zm9GkKgrzJ|wLc-+L}qjdVWB)wT5-N}c@d-cKN zW66hktjk}+V|A*pKD9>rwT8aIIBqnKn~dZA#<8<482=>WIAk0r8^@W(akgt&Gs=w9e z+!6H{9#O9&tsim1CMXk_P-GO zy8qPk_z&b848FeSdZs524Bx(?Op?qb$hM(O7he5Lj#Ys zy+$5)q=DM6MX%fMS{{4$Eyv#YwNKLP@v-JxIX<5Tns_{p$C>+s`g$H`>I}aVUbmkx zkB5?PF!<2-qQ{o4`w1VAV;(x8pXFG$k7OR}@iEijYmDRCILGw{zm~_keKqjd^k)cx zCi1$zl7A6@^?ZYkT{7tmo6vLGdTv zv7Vp8)NB81d93pf9};^y|8gE{zJ|xTJ=R)|Gg-iT@;aVQMC=dO0cazZbbVwS$6+4p z{K|Q(>!XIpdVN-F9M>EAwLI3@H}F`;H=HOr&DVm%>C94UH}*ZntS;d6C<-N&rQ7q~F@v<-1Rc$|s$C?lESjRJ&$9jIt=CSs-oX1}Id934A&p-71 z<|KOHJwVA?ZHxOs|i%AA0@Vjl3RjG>^**Wyh`bOm5zVoX_8RmnGA&XyOssj?M>(ZTSUr( z)UFhFvAE?^zel7#5gzL}@8@wj`GY)m@12n(|4;sHdR(`(VD@Qj2gCV&5x-~fdoI7P zzsK*-`TY~W4^NSNyYTx|ey8z!IKMCA_Y8i|<@a^`zMbC>^ZOZo zzsB$P`29J*f8zJyJdy0e?^F4m#_!?$zKGv5_&t~3*YW#yem~6bXZZaZzu)8c=luSO z--mNQsSCePHTIX5JsG=xYR{IqWwP4Zu?HkKNKe>Ap7ig&PEHdjlT15D@=0R)8H0Y8 zBmgv?BZrwpb^e)5pO6ud|L!1(3r;hAHq#H$3)0`E6d?ON`5sAL=O1SJYsg!YJGETk zW*X_snSPUA09y<+GNGBiMwfrEB_ZJ?E_lrJwYvP?f+)6~?4g23%P;j;uggz9c53V+ zWC@c=fv*1sU4HVm^o_&J^wRE{boqzdQVO0lB6j-yy8PsARz7?~M%Gyrv~%Qm|6;(J=pP=WxdPpX47^#QtmfPSMvI=^y2K`;`x9 zFLrWgQ9H>e>c5odIi|NHb4CmudUmK!R$*RIZfPhbwQp+QYCb*ON2|9uqjHn;k5gCUgHDh_zCK_0FPDg>UT0#xt4Hz%A@{g;-3-M({BR$ zgVLYKf!e)?0%^JtA4j~DcFrgM0`W`e=S<+S{Lyj#=V&ot#yNG2;33*yO#2gvoAE3i zD?%O5I!nH)pR0P;*SmE4-|pgFoe<9hG_JOF+BjXEWCQPvZmaN21MlzRXt%r`rR_|j zKHJb&QLnG3Y5fZ7!-jqx@p1!KzZ0d(rLTKwJ3mohW9Yl$L`LcLbvdmc3lz=zR}PhBHm=+C*s6Q`LD10X*-!N-qqP}=yQoX z^#}+)T7M1kBm>v&vz{A>X8SyGytKEFd^r!oj_NOO5cJ#QhW0C;6oxclxtp2qZxEmi{=j=l}M;xJWv~#1pr?_}0M_&ik`k~Y(8~R-8 z^>tDm&&AYd8v1*v*Vj?C{sro@4gF5)^>tRQ|AqRnq3_yG%B!!#YX8$*ysJ}g=qFRJ zuhZ)D<0|TF4E=4uRsE{_fYnDY({(%9M%-*CykAF1EuR{md0yisVU5qPZj^D%I@{qQ=oj{hMS@9H!e@i`s~ zDW!K_$AF1XAKK4war8$+e+l)O)F)HFfO_ZtU_V_+eGT;?>Yt}R$oY2$+fHyZf!#G4F!C-MCTevr7no~i4lJ2oDb|N6S7#?y)G z>zf*%N<5R-J#{>*UA(Ka)`-ta>cfWqdFmSs{Z8VI27ZusYG_CM)4ikeAN|_Ur@J`n z#n4Zse!rowroP_LucRJ7*x>eDo$vG1HyGvGNqv%`KS+I(VZZxP;(y4{pG`d3z%L=5 zY2b^9XB+rE#KQ)@(ZwYEJxHtO~LFIxXC_4^Hd($V6-zCT9mQ(PRo>kkIo=LG8Y z{WIGCO6rpg{R-;!{WT$$_i5@whWN0)0F z_1T7gA@$kRqq^Ns9ra;D|04Ba>O<6jM18rTk5FG@*bj9U|7#5WU>CKu8x8$mso!tt zAEUm>(7#2!W87!-4fXpC{ZU=Se|;a6uHR%AM>}5?Z08qJA2RG$P@iPzZ=+t{N2Tqr zqdsKl-=#j&u>TYF$%eiwTvYMb_hD)KgIpZv-G+WD^NbY_{Qr~3gi-_+x@Ed4H-;bvK{0H^UL&0{qmHK+# zua?Yw|3`h2q3_s3&TEENif|zHrxIU4T#twPJnI4C=6Tj`;(cjHpO3e%5MmSN9@d^eSQ8|oFE%!^N3$ceFgRT#OD*Qo-05-@tcTur-AQ? zuOTe>xi*}wM zK5MA}Gl)M&ynq${Ch`9ezvnsuwEca=+g~sEsnjQ+0jN04SS~;X@vg+DtPtQm;zNjk z$_gnZo+Toec;cnhKTlkj_Zs5;Hi&Q&^?JYhN8);a^;YUX+9<*c zsDFg`h?fP>`i;at-y--|)W1W#;oYGAOX8P)7{v8{^^8vh*X=~_Z;bv_a2g6%w3yGgV{ImlC==PRO{56c0>N8YmC!2WFAp!amFCgAI zBESUVdLL;S?VLxvlKTI09AGnb{z804qU8G`^^X%j*?ox=es5N2=MCbw9wtC8^&b-d zf%qfTw-CSU2m#oN-|K-nucakq> zDe))Vmqg*`e`FeP?k9e)`;uEchfV!+#NTvZHW9_&Ccb!5FyGIC$J*EW!Gh~?_~?tp z4u>0WJimo_h~-T@F}8her@Anv`}l;%5`r*L}7VH|N2GDzUHU!4oZUNCvLfZ~8uN zy?(pE#j$?2%;R6r6Z_^oe$srw^*pZQJev4o`mg8TH;9Lb>p16Di=AG?^*D1(jo^Bm z(eXL@8o|x@j3sWyX8~~?pHfSFmNGv2zIGj-2N<75BR&n(>-*kyxjv%4$2jE|1zCASE!?dbM-gZr|^=z3rf1ggFLO#DOI(Q)2>TkJTe zt%x1xa^hy3?tfVQm@ym)x>o>*Zul_;<{h!@_tOb7wzbJbn2u) zW<4$@4)@&8i})i}d3OVM%f;s+rik1*=;B?SB%@qi;DFNW=PGo2?&soNosgl=rd~gH zq4lNICmVWQuAd)h<9cG2Ywm-B!#($-uXo%|`~%`Tp0ihp-i+rH#NmefS&lzcd;XRa z=G(+|```Ge*wO7uxBoL%3vRamc7GRKk2AVGjDAXRvprl*T(^e@dd03UU9QJo6gy_Q zz9Np~?nlS-4;w{q#`6Z^A?jbT{zmI(QlD+;uhsVXJdM_`qCRZsU!lI7dfg5`rM}$Iw>eq)kMnv154kw* zE1(@6xAUp5HT0KJub%_b`CdtVy`fjXIj-WYpA*vMQol8>{8?-0b^SJPie0}8UlCmU zsmIB?iR<>Peyd!y|8|>2-$%Y2J$@#?Du7<6>GQASUl&~4(f6_3KpalHAAKLo4O;|w zLSW|?=KBlpXM2FSUgvM)eR6u8ujiKo#Pg`v^Y}$?ihXk)uO)8IFB^%Q^UKh!Vy74F zx9u(QDImUyxbD|m-WI*tuOr0!P=6xroYpA%*~HHvekbv+2|{EMf8yVw??C)g;*<6X z{`W-D>+`-ozX`7WybAhQ<9{7+cl`fC4BSb7*1I^?Z$^9DLA`!XtsC_}QQv6jJDf*Q#W$3f1*U!0WeJS<(4gGTJoiD|It^Wu0&LeGI4|Tu$7xf`SznA(XL*EV! zPsJzG(4XewnC}ezaO%T`zL5H4L%)>z8bkje^_hl#6ZQ3m{uAo64ZU+3<4?Vg!-+25 z)d?GVJ)RWxkcMW?7he(&vEI*v9e3O~PV_et*Xx$Q5${F(3hJLBuFuEyymmxyv7_fT z-M`-IBe>bW+MX`B?qA+{`Wb?!Gv9g0H&*-C`R+*$=BxcbDMfJYzmCsP;%0m{5f3q6 zy*@d~{n1-IPegpV#h?3utM;a!vm8YIMiT3-Bu?Up1ubD-G>L;bu2v8?$JKh`*V2C-&r=4*j^}XVW;|ad9-^HK;AgCQoW^qL z=S_9F=CfS&M!9aMUO$hjXRjuDoeS@Lzc)E%+_S+3yx3`4zg6&PW zt4!i%99|->br~6;A6(&SK=X;#e2~pQ|0Y3 zPV{;mqSr0g6W8Z`Iu2ir7rl;yj^`a02(J6PUgwXQup&(!PZ#I^k{XGs4I8TtV(j&{!H#ItFC67|W3zLI+V zoVnKDN`0oGf0BCr9J^xK&lLZ|hF;h2+hxJ}Jqvo(K94L9;<~*J zC2qF2O~gZt!%*7!l(@bQqWi_9*UN3e`e{LtP{VAb8 z&k&zTKXn|ATox>^-WMH8T(|S3v@@G{n0B=P?-1AiKScfa#CbWyd6D?*f02B5(9U+^ z_uU})R^t1JU-4JLb-sT*OX}-Q;;&PG3h`T)i~eHbnZ)-JcYhlP+*INNR*2sHO&s8r z#Q%MV;O=kX0N3lW4fhIeuE%~Lj%m^T+(6ja&fGejP|eFPl@|uqQ9SC_7M-cR-*Guho{A#65{8< zzUnX4#7DARIzAJh6TKOq8;I-p==Spzaov6{r2YR8*W>mA;y=D1_Vs*SPkh=2!TZtA zzZ1Wa_%Pxx6aP1H_uZY~-Xz}iqS(2g`u)To+a!1e%iH14vFqh{;HqBq^I{!nCxf`d z_fmw2UqU=YJc;-M7soz~QI9LB*YDfV`sb;yHS{~F&ot~Gq`uzJcTZ-!GW<*@USrsq z>f+d+G3-=Ruix{L#B!~qzQNExPrT8Zirn@-iSwlaS z`Ub;(HTBNw;5@sMdi^|KJKBGq`XocYllmsw&!+w$^&vywJyrbI?` zpUL-&==Fa!^_hl#CG}xL|2*~ChJGjYHHQ8m^rXi zt){-l(66Lkzi&nBpQpao(C?(a$*_Nr`g%j(9T&Kj|N4C`I-cn+j&^S7r&6!q=c32A zYU&#d{Yv7E2L3$pCIjC|`}+Ma+WtZ6_Z#}|1H^y*ei+Yx7w?J}-3QyVo);#+C*zyB zuGmgI#CEICI}Q-n^MXF_$bDbz=<^PJ9`OjDtJV5PAXD>5=Ut*l(A|&TFYWw+;7>4L zJzxBfxSlWczUVt2i$0Hfz3%GQB)HC3U%#j)uCHI{bQKG+)`7VR~SnGimtatq$Al)yX zWW6UD^}daI{hlDL|C;)cp>LNa?MlB#Nb65=ag0lbehBsYJwsYQo%&2ee!p*~Fe zTK^sO^@hIFAn`}PXG-f+TpZ)Jp}&y&8pD1C^$mvpHtO|zs&c8%lkDq$| zf95%2N3Z{NJFGlUaNQ2|_@6geaBb&q_^I0G+r-~t`+Nv^toF8*<<;-`>PG!nEN_yf zyd#IlF7NBa&GLRhT>IG$6M^z`(g?Bh1M}5!7%@_C9S2?Bx5vaTZ^l@`b$NB%{y0u> z9XCA=BxehbX77IVeH1f^>v5nP{EQVhJubDMDt63z{|oUD?dy6kzeMzA96luO#le#A zLo&CrtRy{iBNPP5r>t@lP<6XR~ zlWCThdi`Fz8tN~mKHJb&Y5VOY{vqmbp+0Qr*HCZ17w=8#%MJaP)a&==Y5Rv~sB&Sx zF!a5N*BbZ`7sqozM+W^2Q(tfBuOhzI!0TuylXl8k-u2Ws82TO5HyHW;M17;7?|iP5 ztI5DqT^!H#(~j=H+0^eh^rgg|$6}A??Ptes&wnAVGpYAMX-G?N4{#@ z8%g|G#z$WtshcBu48QJY4fXF5cYjj{%-h5-S}6Kgss9dmtajc4+#Lu0Amz=b{x~?S z+DVcT2krlsD}(;){=S#EIquE6O7tPxPlx?j{#;A#-GZ+3BvV}}20 zsMqgj)cS4ICmZ^2sMqgr)cTI+OMEg7eP0*Hx`O`bd2>AV*@nK9dj0-KZT}|f!-jqh z_4@sgTEC6@azpj&yGVR{*4B%`_YSwrN1n8*Ic6ZPeW{t)&0y{KB>b13_@p+DQjF^}`Tr&>RS z`dULjmwNqPRo$=uMt!}ZUq`)u@2b}Shx)aK{(I{6ds($VowCIL21B3h;uz-*|1Y4v z(a`Jl{4tLwx{){A?P%h<-RgO*g1ByPxuMzvYeoNs`toHDb1z!hTwda{n z1?^~mt|xB#^C)rCpVR&sw6oQc@5dHg=ld<~nE58D7hd6G=6f@79S7YXpCGRL<1t7Z zD?UHLpEk}hPTlgh%6#AxOWzg)mx|l=10r2^xP12@ei=7JL$s7kytr?Ii+oD_JmNzK z3-A{4i;3UCh1l^@Y-c|4cSp2wl|NBmM|{U{!H*-piuh*(CEw%e=SJdFE*0VNlFWIZ z_<0iqSWf&K;)Bl>N1mkpcEcqOPv;2mAn}ulH@+djdg6nKH#Z5O$LI0Hi+LWc$Gt0w zKXtz3yM}h|B;IAT0H+dvk@(z9(eER^o%liSue!fg4(=!7b1oC%Gt?hFLdx|$?`LCJ zoj(zeaHG10`Vqu`yg`83j9V`8yH^UZoBFGXUwww)ClSA!_*;(%py#QliN8KU@J>>l z&Zop@9wWfz%=cH~zYUW(=y)DGQp)w_7h>n|_VS%ZynTZlpG!LS zW|Wkx_hTYtvv9f)pSW4@Zp`;|;sct+K=;FiA4&Z4XQiMWnD1obgWebYDa>~|@oDal zvpUX;)GsD}r+R@TK68oRL43yd0z62(o_Hb~+PlP`Bff2^0Jjt0PW+`~r9ht&|AzQO zZ6x2?y_AewpK0)vu)XyQld7Q-OOX6#ZSAQhH5=rK~ zO#Bs`$Ec4U-*yr2lq!Dmb_(Zf;$Mst{3hBtdW`u0uZe=|`Sf(+bFu}$PqJ~&Cw>JB zx{!9J5$}DL=yT-DnN9qo$pWmSoxc+QfCJbR>hB@`NEeAiK|4wC5b@3Kk5uA)>D0eU zyjO40M~)T!F5-K5;b|J62+XC*VBL*(jZYP&X+}z(c29rv>;sViKBVW#V;NVC7uOQcCVv0L;?K9lXAw{7DfoLw2vJ46afrnE zE}@;9h_7EnJ?ran;$I{Qa0~H&6Tj<1(XVE{2Z(ojNAPo5u8ycbRo>^qB2?#->eGk# z5;lal1a^iI-?3bPhna5y@kT!HcA^x+nL~W(TVi+x@fF1Hxl{nXpZ_xPygIR;&3v1P z7jgpE{q8H`NhgaV%c%dE_(BHeDEiqM232{ld|!mTJ=Zyz_^%zrpEZobpNTISB6=N% zF~sK|ErzeAzL5AQ_*S3KneX+)*Nqn8>%>s-@*NJ_qWxNbQJLklO;ZDX}^;A zk(}>XZO$#khxHcwJ!$_b;_d0@R@(U&@x4n$uh(y%6ThsF7#PX&{zg1KM+_{Youjcn zQ}uf@uh($d>Rd=Xh4Do5a6e(t!={3#>8R@><;4t_&? z3-M35QL&rrtM`dN!f`Cb`0OKo4WBDt$$r{)qLk~tIuUYOa#DzQ<;1(4`3@)k+am%T zM|=kHp$EjzhdFO9C4Twe#eRr>K0$o-Q-Yt{UJPv@{_K6y9^Bt#2K65C6J8hlGq|q% zmiU8QZ)ehu$2(erf6=|z0N01@hTdp7Y`YsH_gv@@0XDOvQB_&nm*R|#Gq z?mA0|_vJcDue+WgJ~Shk?>6GIIX?V__CFy0UzTgIWaI25{uDbzGVvoOOL-HPiX-lC zeZ$si#NRzy;&3bN4<^3%8o?1P_me}sDnsn(_{`IG=7??;?c7K_ZJ_`h{+!jsue(_6 zEY6bK389k(8^~Phx&y9AOD~TJ4!O=BI3K$i`MbEpY5cI z_|OSb&<&Ezxt;j)>~{l*uOYsg*H!hn`abciZW3V!_M`8${#40V+v#+%_`i_L=MtmOEV~dH8B!0iA{qY3e0LFV35$La?R-Xj#d#99 z8tPk!kN>k6zMXy^IaT8D*m%+Fc0Pvq&7%bWkopD0&pci1tfQZ`#6L|DU@!HL5^u~A zJGwu*YP-9e0af+y9Yx2F@4NN4Jw6mx%v2HHaOahB;Zp zujTsuN=fEiN_Z9=%;w#S*0~{8ej$w&ImuhKfSBhIs8u7`6%8KLM&2b|;Ec&asPSfLg3Gq%F zl{z{eE(weNibjPx&U*QB?hFfl+XBh=72>ZEzj>?xT)sG;6F=o0!Sfi01Qbx!<5@WZ zM1*#F6R++dcoy+BI1W# z8I1GY#D{YHzl!?R#Ghxw*W<%Z;t3at9ld_&kuQGs3k!Y`?W7W4&;7mQh@TIa4+@KiMN>~<<)+c6JN`H+JV&HMf|eWi7xWAd^t}NAHnN-97dfth=0d^ zT|zrw5Z}F4@-3Aw=Lh0@=L;~O`ac#(9QHORxX3cD1OG()&)12tl=`8>`~FRUNs02E zL;PM2y%u31M2 z-x2*^^{<7W2!1ombtds#USH7hA58pXHcX9QM117?Vs`-jnMeGFHDaHqJNcQ$K+C!+(-|KOjDy_{Zl-0IC^>nZ#dyMu1x-nR7Mqv`@rAmVGOe{-h*dcXH&;#0axzMa@F-Ui;r>EY<#b)^~SGwRR4 z{H8wj?!VB^lK6DKQ2b%Boo>X7c;Aa&pZ6txX_DwU4Lc)^ka_{ehl%|Jn!b|mQzgpm2}bR_I54tzw`ZOdl-i&hz}he z)NdgksgO9_Lw~*|KIv)E|CM%}%cWeOT_$>VW2Z0iI`*TGBy&a)@A9YsdfvI1_|+RF z!!y_%ONrm`FVTN;n0((%{FR|{tjqf-@tlVR?<-%<7UF;6M6r$jzeoJY6ahGVJ9~*w zzB$CJ2aE8&BgD``;@@y$If?fF zN_^Q4(YK|Y)x=lbD*%_R&a=ed<_QWfKRfRe?|!D(siB=C(NNVqvf^;Txn1R)LcIOe z;t0FFGYWW@gwD<{JRxXDKW7q8?k9l87Xv?<8voa=z&j-z=QRCODtr*hCzU?oDCg?Q z;t!`Qr;&Iz>s_CZ{9v(vSc%xtzoV-4eTeJdN7eWk;`(>0{Y2%@rt`%S?N748W5j2e1;5OK&jzmitf8Mtw7E>_k+1&USnbbh;7N$*4#r=* zu$Fe}X{Qf~w`u3+Y-#@w693xO<99=nBr-bR4j8ADKVkm9s>XW|4-wb(J^*;EIEAH(bv(-~c9sBF?W&v6t{$YF2p5ufw~-89CB7+3j!VzK|e!V%{&9i`s;u>&MlDE~V-2M!Z}u+s;)vL7xeGByiFOs-j&Vu#CxUI$+bKE{1c&zef0C(r#r=&&c`S%iy zbNtu%0*jp+E%<#3N4c)yd_9=udfv6)(@8ek!!8#;&Ixmz)cGEyodg=tc9Q0Z|MlvJ zV)4=XWZ)qe3H~$i%Ghx@pLUiR^W9kBZvX8kc69$O2OcY)*HYh&+3NOFXVI^rz7Oqd z{cFHG;eSqp^gG?(-&Huqv2vqd^qedB6azm8xQc({IpR<%{kn{J(z!wWYU0`F1@To1 zXIlT)v((pep4Iku0at#8=ua{eedgj_o%(tyQ6J)M=Eb&utis`c0#C$sy`KqOwcC25 zeU7!*nNB-HMo5O*&Ro|Ho`W#Px7%GD^QjTfbuQl33GsdM_4NNWWuJfhzjgvw?PrkD zehv`V<@egld?|136zSJjwGnH5fG1)7_6z;dd7K4Y#kq$4sFuV@s=r`8pUHerB0fmj z2mZZrUtcD00IjSG2U*H@pcRIU83~-+yAw~fW|@CS0M}!bKl?nT zM|n4~yt;m?UA%+i=!u1i76VuPH)PDGchXK{rU-T1ULc+{B#3_mT&=g8jrN>~3mnRy zA=GPsdH{FVQAYndTj3Zd9U~6oTzyYxzfr%=#Qch?l)h!o^y#^IMboQtXF32XtLDs@ z(Kp{ID6A-)SyWk7STVh-WO{yaS!rPA4jZxz*DPORFlXof#FmC56)q=9H9FBZ=>5I+RrbX=Zt4HQXr4 ztSp?LUszsMR90Hqx3s7PK291pefp#YqxzNSj=6AZa(e#Q%k$IoONP#!Q_*km^nR_= zsjbufTc-!KPN%g_4{V(t)HTw7l}-Sm_6* z_j7U!3Ze%4C8zgS2`5$L<`=@IvV#UvRghU(UQskRx2iC+vY=>YQB~!fs-j}uPLN=5 zLBY^jxfK_ssMJHt%B#l}mZ0If-%;}^196ijGE~w^z*Pm)Xm*;<>hw7&$^DgVRB6>H zH2qeDX(`&h+{((r3V-88jE3h{p(DB3MCs{s`X#GGZ1e?X#l>*O*Wye2r{|{UPAwlW zq#|$1-1OYE3x<}Do?4PTbGp==otG|Q=cUoxd1>`_UYfm~mv(RGrQzFoY58`ZO}`a^ zwEb2D@j{3Otwx(jTgz(c#%wv7s*uJ z$6D!Msj4HEmlu{6OsuLXDxIl&TV-`=eo>jP)65?~JgsEzgbVv;7o^OZG=5xq{;-i# z=3U4xV`Ifd8!I(!W2KsHtkkiMl?t}8QoA-*s@BG`i&$x?RvVq#dZAKVFI8&m#Y!FI zj_hSar7DM(mCjW_c12-%Zbc!$adWD2)d+e~s#7?-aJFxrNl)q5&l@v+wSM7%c~_Lr zzoP1bf=MIh7Ehc|mY%nuDm`}|tKCXVy<2Iicq=VIveFVHD=k5?(wH1n-?sBogLXcS zcm3kH*Ds!b(mL(=OZ&9){o|Ccf1L95mvP5#KVIF&soef?;@3Y;{078{-+(yr8xSXc zX>sC*6-le@CoN9=(&EG?ElzyW;>0H{PJGhh#3wCIdfpOw9 zFiw01#qn=Y9RCKzslP#S{2LU{zc}?bC{F!h#oucC#QMK=K2HAW@$!$;eo~U-G@z8^ zI5m)h8x~qEf;zj1BZ%i@N}N`bk`m8JE_AKqXi7>vC%MpV#X_8>l){B>D;DBZW=cxD zKyjgK&w>lxRs``P$%Sq!7UD&c%dS=|#ET@CUaeS&7s=Fkk&L%6N$D4_P4t~6@%lx))mTcrC{*Cykw?^5C|@KOfG>)iw6 z_3nZ3diS7sy*u72GbP?KGX+DP|Z@HQh zZ@rq59&Zp!kJldJEooDe<20Dmk}j2{smHnQ8#dE)B^=A*Xy!mJen{qB{{{Z^m@wR;-ZMemML3F+7ujsf%H-muF2-10*Al3TP~g9hPz zs0sS_aQ}Xo(0&gONOrHJmE=~<;+}A3`JB?C{IY^V@3^w6!hfYut|h3;f7nhg zN5aywiW1+3a@LH=$whO}B1X&`nKeFT*!=SJ{Jb$Ur}K)7l}^_eCdx1x;F_oZdR|KD zgfYpp=Z+mVeC*&M!_#x8m5#3(JVjrcu(2||+E|%WY^3sM$jqx}>7xHch8!M|E8!P3uu~KjwD^1PDN>j73Jnym6Qdw5Ib+>uOV@=(<<2>83rj`Yl zjpmxrO1Bt7aA8ww&qZb1Dj_xvdGeHw?h87i3vQ zb8(9UP9F1dEzVuV_*MO}?u6f;hKq8Giwd%^rZMdI8x*5$&nPMmH07b=MwJw% z=ME}Loj2`*$$9gKRpbvZA3d1Y?rf|y3L7hJ!p2J6ZLG8i8!JuO#!7pzv2q?`V|mHe zO3OKnm6pD3rCW~|2QF*s)+5F{8LX*Wj~VAwYwFgc#*JNDYHsUV>DHr$E!})9<*IPT zgavdZZvt|cr_)>lrvsJg1B%qmL36PBaWSRD7AFVw!qT~Oqxba?%r7ax@UB-UYCIn| zEqlb6%E=REjH((rv8dmKxuq9O8l;DF3n`7!sp!pv{KiJ8Cb*SR&ixCR}IM+ktbKp7{pPTYvSp12KMWh?o`Yv zEyPLD?>M!?mx=p4=jY{C`djD~Y2^dP&(9j2iyJE@RZp8gW7_3IvqtL$oQ;)X$Hq!C zu(1*m8!J_5V`b2=vAil~rCax!V~Q;`?})O|ygXv1Wkj;lt%r=)J*=r)4;os9Iu{J= zuwVlr-6<%m!v9s5z@3t4?r0iC`IXb>;QB*m`K;W^S>A+RTv$4pO-gXw=7<}-ii@hM ziVKI878K={ruC-)cXQzoX0Rh%T$Wp)+o?{N3iB0ZRTE5mxHN-oQqoKu+fn{}`r*Xg zUB`I&U|U$_qw|mE(;p}OMKh|#6=BcGO`<7mX{fxCf1epvOUj9XUM@O`+K~d&ub)>= zkAbmyg}GOZ1)DmrsA|^4!oq?|?WL!}b%Nik48QtgA3`MplFRin6#9q29(eYf?h8*wX(%c?5J!*{hm z=H{J`n^z0--Me0=NzP~+YL9Jr5s21Q=nZOvrleeLJQmKLlUr=GK3wxCoITOK>95i$ zEu3G4{ft&}RU2}1Dyt?Gs_0O6d1YBC7Q5=2pE}vEs4UE%G1GSfId0(8xsz}bH99|K zNb01?slz7^n~|PBQV(rbT1E*g&FR@@oD;N-=4#Mtomc+te4Olg)!v?7h6*dsQO?fC zi4HFUThsFzu$_W9L_VjVakMub4Q}>6>$}KG@E*x1n zUu`7I;>df9q)=@?rw^D_Sd1$tSmWRUkrJE>`lg+NVWWmsUr?IX?~1YGhEw`kkXwkq_i~)DS@((5~crB+PTCylB5OLu)q!i!N7&#A#Dp*T0ojs z`CY!X>+NpWqwCe(buoUVUG8={T~52Jst;%-#1XMK_5ebA;2|LdNPvXIVGeK~aoPii z5vM)vkpl;Y?~g}jL^{(o8cOO;J0l|_;*bCR28 zG#I5Fc!fXKvrILUUu`aLTdn%R-YBps=lSh= z=K1Y<7xUZoM&`F`y2x+Wd!65|w>!UG?|6Q@+4MCJ=zU-FfZqBw4?G4#g8*yZ@E8=0 zOKZ&=9)qKCYpr>M3dZZeD9j=1Uk5kcPH#D$P?PyAx`BU8qGtqBCd(Ta^_~~s-M#L< zsb9?wE>0S~=x8xLoV>7zm)EKSp>BcRnjw`6Z;<`jtSdz}!{q|Uu{9{3&8CEZ-Ukr@ z$6%4%9Xw1Y&oHHWyNBDUaL4273mL1j_5A>No<{3aq57(mcDQnhNBTFFb=)dcYC+Z z%kyUR{BnNuqP5%TZCqS5dd-gBw%lfo;7{mF0F*qq<4$v(ebV|0GI zCjR{PN5Et7I={ch?A&&P+xhK}z|R={`TZY(AD%!#$o&3~z|RyO=J$UDev}@rAO8{f zncVl>@r`dczx@&XGft2E{*S=VxPSBeKZ1XFR_`SDtW9vrIL{1i!<@)qTGdiWlIs&E zKu?L5J(QO5Xs&8EIc7o<>juHl;R=mjf7H8dZ|}Vumg=R;aT%A}&6#CHd950!@>(@S z@>(@M<+WN>IeS(bm-6~rH95Pl#-qHxv5K7C*=R?(y)_c$Hm`?QtQzNxy&ht*TAb6H z(9zB9GQdTIC;e-=N2{*&Jg!v^iLEI!Ie(v-Lyr6(q3@8(IQW z*NerBlO;$Uv^`Urv$@^r?433bcRRaBq%aY{kkb{Mm|kTA2jsjvj)K$fbQGw3zoW1@ zAK_#Uqq)k7!+9NK1_Udc82_AzqVhD!w&=f8i55vruH1kU$&Gxu3^^{v_Y8!QN`N3< zta4ga;wo9HoU?(KTR&>T!<{9C4{DCPtz+Gy>)ks`GW*CKB#@AHaeMPszIpBaCa6ak zK37~iZQf0CKhzLT zgS+P>SP~rYW?G^A7x<8M;seJ&eS;uJym8K2kYZ$Vz6c&jJ2~l&0h(&Go_#_OhALLPFyh=PJi?I9Pu=b2K z-SMPKSNcROXu!>Qz-R;Gol7E{N6mg$(V*D>$7TJnBossS-3)1xQbPUMpfww zjVj6lNTS!R3IG&ErC5%gV*_1D;b`(C*=Vqnl~D~>(hAA41Z3@&IP0nq&p|jzKDKkW z$SDMd61T%i=)kC4!a=OF-wBBa)8&myFGfaf6o!*9oRd@M?`c_|CFFp)OniGf>q}6x zpmR51XiJNF4m;a`H7&$@Oc0)EX{QCEZoqg1VKd-gVvAX#w8&QZ(>Ea>^1_n| zQJ%3GixrCu)hhAm6=Z;`E{nL>sbLVBh)K*t;wmwWn1A~$!HPiO3n~kv)>z17B*Q?R z>Vkx%zzG;11l<`Lshiqf5ps>%G`t~GKD9!$a1wH)c4CFG)-BGc^qBUzH}A&bWbM2{ zwmOqm*p@IGlGSl8B@io@h$*|k@mDz*i|4Z?%9R z1%KI#lu}}-t|4VkpqA1{)EG2N?h~sNy-5@X*~~&xEux;%I!LKV&?$O1o=Hy9R%jC! z5^gCfnbN$}B!#;ic9DJer3B)&GSy(ih=e>O`&cp#%%tOq6pPf8>fCj8c8oKDI$8%T zss9ef;$vEo9Ap=MvX6-nr?e9^RFgUn^_1Khp;54Q@Yv*r?TqJa@!dW;$~3&29&DQ_ znmmLC%k;)daY>(FF)>KKsWlTtup7zPrA;hSnva4zM6V3Wr!%E4Q2C+Q=V3I`OHGzT znwwT0N2H)E=9i2x>jzSOb}!$ zr^*4-BS~4CPy}@XCa)kh3PZXUtt`hbOrzx|vM%Owf zTSj)`yWU0^s=PVWMsM}JmmyVATK0Zft+&dKDI+?|FeufD3+KSf^29m=NyaJo<1DO- zx~Sug?L9NKgm4fLtE9?|InFUksrw}aU1Sx?)JndJdB-kaxpDeh#1NU2CV620&yuXit(H@xb*14$jJm-qGm!T z%G6MNl=QbGt3GzcRG|7`ia|TF!ricz5)#+!PMTF9tzq3ETA7;O0_8xs_6E$$V@8IM zP!lV#5IMVPYCZ?xg!50<5r{TE$lC3KAa0s zHe|PbYCGxFSeROdVq_#8B51al`_-LDAL{ieXFOPr0wqk68P*!O{(E6ay{K3vDJXpv zx`f&h%vc%*8pduSoB-mVUz52EN6QE~pu_=B?5E@5s~~`$+~t?Rj+^fM-M*aY>VOl) zRT)c5XT^e6(^o*?3q{Ec6-Z_YBXl(NZcV8hE2JvQ*gA$q zVwj=zthb}}1Lo&P%ZW9(^^Q}T=wYscV#4Yws^2|I%Uv)(9-iNY2GCNp2pLLs=PFi; zw&ghw)LRX$Ti?)5FvPne!gRg230+!=r7h0=6-(7sw2PROvBv51MWe$46`w$mAzJaP zqmY^8^yXlo*^kEwc~T2r#oopxr@FYZ!K`B5=;L0MrgSN;gM&2^(5B12$zo4t?|?{> zcj2az(yW*%Rnw@Kcn)O-UxUO_)d8tDq*z%6b5E;0GWLmt%qTYJOk=8_GrUj420g}? z{c>Z&LhH~hE-ZuGaLL4tB|7vhoG&h>T3r}e{%x3HMTEqVyxcAP_HA6S;DQ2LtWJ=c zr}e{CF!^LCCtL{`3JOxqJ<#qSXMgKOWN#i*u8IX8bfs>r3&NOL#CV0vEeA752@mMI;J=Ey2XlRW_b-At^u^*;(9VampIYG{}_S zDXt|^9pkkXH&BRW?U}>~ty#FFfx_0Ptur+2?;G>v`EaJHkOpHVpm+2Pn$v;QxFf#{ zeP(ekz9+8vTq9P8@Ly`xau*+#U4`*=D9ctmP15v~LKI2!W?p#zSlTLfHkZy}4;5Ua zipe(BLE?H)&Ge}B&ND4J6GqEFhetG|uwbGzq2z9@g3Ue;eE(*Oelu(nXQImUgwF6 zGNI$`(>EvQ#~nF^AnfY{*fTVO8pM@7D_p5+R-)4hS4m)6t^sKuqW+emfeAM>G_`xr zsUH!hc>mqecp``?uxyJLTOW!b=ql%ec;wc=dKaz38McBc+g+G5v{TM8){u~_MjyPq z#slpNKMs%3b5A^p!i8;;guT05Z-c62r^HHvgUx!~ zU4Y-xx@kr8cO+0*2pGm7Yd=#eOTxvp{!%Q##yw=Q{JliF0(?-C$uPpUr4Uq7d1F1e zz5$)Cf43ZN1ZpEri{gd6cYFGp~{G2B5s^4TLZFQmNIsklsC#=vC!1RU&UZD#bB#$&zkjW z>qt*$=gm>`u(b^;OczYnL4aBfu8I|Wj*wMamimcbzivobs(%J$hOb`)AeFF{65BUFMReTWkJqF7AE z+mj5ci%8L@CGeFtxK)5F=ir$N)C^5{=fR{|ohW}86E2Ebnz&LtLA+vRqTbofNObf>x3uma zDi)QyCYgG%W^C)^R=I*i9#5}ULphsbX?Nii1I-wdp+W{y)f!9=%*BU2QRMc4S&2;#Sff|29nS?MUuOOm1HL+ z*)+8;+D9kaLt&xV(AVM51e!DWBY<`)JLRK@$*PJJDE75NiYO(EFo)s^)`GFkXfy?~ zm!-nGqWQ8Ew4#0<&*dkR2Im^(9GF(%E-n&Sw5f1N9ly8~Ag#~?CfaH3`JRnhEb=~E z_r~sFXC7|WqPlFJ$qBMUm+KI}H>dS7iTp|(sZ<#|^2-b;JUA_saI+YoZ4Z$NND{+B zHLh=>Yk7LuRu`d}U<@_Usc@-fvqg4Ec0z}_R%8thWlX|oc^b?BqtbxXJom=IMEf^{ zGKT!N(6vwjBLd&pY?7?%RIq@9#PA3ciZt_2#9$dKI(~XBv?NQ$aD#(jIme_K_s#1x zPcQ;CvDLh-P?(d9tAmK5_8IHQ#AK1@Xq`67Y1KB^nvJ!-Lc?<0OM2~zs458qtA(TU*85VY^Y&=8chuWEC~fcWwH{uz&z7&=zaMpQ9-qd>hZ-ib zzQ!Uxm%@Cp7#p{So#0jSlVQj$gtH{}c|$(h3Cw@&z2f0D#~bS9C%Y$pE*NxoA`tNg zHfycz-*0rcUPX&nJFn-(cddiNix1IV2mX&S)(2m^93FXtdKq^{2zA6XnuaLgeU(%j zw>dzZzS};G?ygFQRIvA(%^ePn4!W03QXYe0cR5*{MHA>)vtb3yB!N;g^eRM6ge7^e z?zG5~PJS5f{1D+Y4HU;Cb~p%^3-58Y@LbHO6^4rdMa=7x0;I)6PV6j*D>0mVPkTG# zB3`u&*^n)%Y%_61b_ZX%qi`a-o0eZz8t5?cmKBf?Nkzki+`SDZSP08aqc@uwpM<3q zc^EtIb@5BBBEg@{I!Pj(0E?zukaJh$WQexPe^n3Kx0DD745KElUm}RA#UTw2WyBFH zdycL#kT;lTY;Rl@qIiN;Qq+iiWg}H`*+D4wfCWNX)SRIT9ecnW+Kw*ZQUN*?T5<9b zH1N8PL?bk_@oGv42^3kmruZC3a8XXJUfrvzoF=P&aZuvfq!f4~Dx5qOHn6fdSe5!^ z!|Yb&HSNsiA&Kla9YVptvv9+)oZjohY2seDIg$wk@nFVGu70^&sl4fJ28~|39lqw| zjobb0==Nnt~O}Y@w@}ZSV^CNSSR=nC*Psm^G1$wP?CU71$I!3d6z>92s^lfQa}|Rr|~gM zu|&a;V``PEgG0gzA6H6!DH$Ltqq~Pu|=k&lE09X{iTcpG=Iw7i?4JBOW{LrTW zP>CNQqx4utdVP2~FBadA4t6(RyxiX#za4d7pR|$GruoI~>%)`n?aF+oU3*&__b#pv zh9yK$r1DF>`3xq)AL3ie(z0AwEyrp&xN4OkFo*D}3=`kuT(iT^!?(?9?t3WSr|@F=6KNcZn@>L(2*qFkdnyU<3*}{kl)cfadYWPo1CgG+dF%RpM*n? z_OjF{goEeUBM1QW z-l?x5JlYF&%t$0MRQJ@`XX1f`0v+XFaxR3er8&SVZdB@+vj#WLio;%Wda=VVaBgm% z)ta-`vi0f$b#16@jCj9fXII};-GH0=EHBOOh_I?U&rHC}nfq!Th7A)X-$YGYVTKwf zMOgT3100-(9MRXU|)W z-th9}))bO-V9eeo&Tf3T!hH$pw{|kgkxcZ7u-6q3k%$^B-uIS6Z!i1TIBC5*%BRrf zCy*0h)+=Q7L%5Px;61C^oe|-7O>e@Th?O)iHcKzB-}bJb-#vU7Z6>_)&2W2CKQ4|3 zABtPi+x^zg{q6%Po3{I`(S6YxUyQCEdT(y7o7Izt57*;c!Ndr@?o-UrW37UlOZ*kD zg7N5DkinA{C=~|RNA>GobTfGP&^q6m>>S*@yQm*LX8;lM!EG$$Rm!HfTvw^EXR)QU z3hD?#>N1pDB+7mP1EXR@g(45CI)N)-7J4cJe z=ON*2M1D3gigt?qBYBkSmC2ABSWmiOiNjzt+p%`HSI2~J@P3?#d?gN4AT?gD+~Oy$ z54TOZ+Ny4sso0f~3Rr43!NRlCMx^Gm`e1OjxFKCm#jZLXRhzd`u!U};O7pJlhelOO z^H9!HFwNlaXz$8k$*_4!IG?;N$?Cpo@$hswZVyK9_Aj@puWJ`aoWRDfct$&-MqvK0 z*o4EVjn%T8S@~ypWvzrpMfHpD{0=o4B>7@BHF9BejyuFsU+fvdU+@@c(7hjh1Zzbd#L*l&ew{QCN}8h8v0_@< z#MHK$P;aY#00CUS?Z0~7op0|%_1CZO;Xw$}v>r3s<=0vZPodRCx5src*Vk+NiDZdj zJ}W$(Qu+9FG+jQ$l%g77@!;H2^@5*{2j*6DtM#e;D!qDKT)Ks)Xn_o&uXy%Ui}6Kz zUplnSgGpC9m`^k8wWqS@g{ORcItuw9?vKJ#_HN8Wp{Trtr@^)PJ^JebFR|Zv7ICFt zpnslT_p_g+uZ+lR)a<%9<32MA7cv-k)T`ASB!dRQRQP}Y{W@>>3I6@0dDF=^^ap$W z4RhaKi>_Xzr8+5 zJ)iFXiuV8h^2^Z9|A(f(y>6KPKVz=x`FFYhbNsXYKQaT@>$9KM9c7IIJKO&geY-H` zDnr`-2S2MD?Dadg!91Ua|1I~wbml*k$?z9n_1A)(zkQv({yy#h>-7J^^tadFGyT)^ zH@6Fa#0Ocjec!X6*Ddz?JwY)3?D*;R{|RlY)qmq(^z-&=(8)foU>$$q!@rvTcK-^WneoiEbm~4`C_M4M z7XB^Ozw*l(!uC3_h;I6&;r}10{@?#y{e-=K)bj_laI<&+lMiz58|K9y|GEC~@gM8! zYXeXFe7gUC(eMk>-#n1+pMKtcx7Q!j?-!l^pP2DKG2`3k%Hj0s|NCZq`v%+3UVk;!|GWR7$N%mR^!2sA*zScV@hAP^TlANA z#q0kk-QkCSp|79Bh^?Q0qA&6P-!|iK#CP-u+uyLGxRuA%pL tablename + return self.name.toLowerAscii() + +proc renameTo*(self:Table, name:string):Table = + let previousName = self.name + self.name = name + self.previousName = previousName + self.migrationType = RenameTable + return self diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/add_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/add_column.nim new file mode 100644 index 0000000..e833531 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/add_column.nim @@ -0,0 +1,8 @@ +import std/strformat +import ../../models/table +import ../../models/column +import ./baradb_query_type + +proc addColumn*(self: BaradbSchema, isReset: bool) = + let sql = &"ALTER TABLE `{self.table.name}` ADD COLUMN `{self.column.name}` {self.column.typ}" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/baradb_query_type.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/baradb_query_type.nim new file mode 100644 index 0000000..245e055 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/baradb_query_type.nim @@ -0,0 +1,14 @@ +import ../../../query_builder/models/baradb/baradb_types +import ../../models/table +import ../../models/column + +type BaradbSchema* = ref object + rdb*: BaradbConnections + table*: Table + column*: Column + +proc new*(_: type BaradbSchema, rdb: BaradbConnections, table: Table): BaradbSchema = + return BaradbSchema(rdb: rdb, table: table) + +proc new*(_: type BaradbSchema, rdb: BaradbConnections, table: Table, column: Column): BaradbSchema = + return BaradbSchema(rdb: rdb, table: table, column: column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/change_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/change_column.nim new file mode 100644 index 0000000..66a826f --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/change_column.nim @@ -0,0 +1,8 @@ +import std/strformat +import ../../models/table +import ../../models/column +import ./baradb_query_type + +proc changeColumn*(self: BaradbSchema, isReset: bool) = + let sql = &"ALTER TABLE `{self.table.name}` ALTER COLUMN `{self.column.name}` TYPE {self.column.typ}" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_migration_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_migration_table.nim new file mode 100644 index 0000000..ce9dbb2 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_migration_table.nim @@ -0,0 +1,12 @@ +import std/strformat +import ../../models/table +import ./baradb_query_type + +proc createMigrationTable*(self: BaradbSchema) = + let sql = """CREATE TABLE IF NOT EXISTS "schema_migrations" ( + "id" SERIAL PRIMARY KEY, + "name" VARCHAR(255) NOT NULL, + "checksum" VARCHAR(64) NOT NULL, + "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )""" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_table.nim new file mode 100644 index 0000000..3016f9d --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/create_table.nim @@ -0,0 +1,16 @@ +import std/strformat +import std/sequtils +import ../../models/table +import ../../models/column +import ./baradb_query_type + +proc createTable*(self: BaradbSchema, isReset: bool) = + var query = "" + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(&"`{column.name}` {column.typ}") + if self.table.primary.len > 0: + let primary = self.table.primary.map(proc(row: string): string = &"`{row}`").join(", ") + query.add(&", PRIMARY KEY({primary})") + let sql = &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query})" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_column.nim new file mode 100644 index 0000000..1dc4a86 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_column.nim @@ -0,0 +1,8 @@ +import std/strformat +import ../../models/table +import ../../models/column +import ./baradb_query_type + +proc dropColumn*(self: BaradbSchema, isReset: bool) = + let sql = &"ALTER TABLE `{self.table.name}` DROP COLUMN `{self.column.name}`" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_table.nim new file mode 100644 index 0000000..db93089 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/drop_table.nim @@ -0,0 +1,7 @@ +import std/strformat +import ../../models/table +import ./baradb_query_type + +proc dropTable*(self: BaradbSchema, isReset: bool) = + let sql = &"DROP TABLE `{self.table.name}`" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_column.nim new file mode 100644 index 0000000..5a386fb --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_column.nim @@ -0,0 +1,8 @@ +import std/strformat +import ../../models/table +import ../../models/column +import ./baradb_query_type + +proc renameColumn*(self: BaradbSchema, isReset: bool) = + let sql = &"ALTER TABLE `{self.table.name}` RENAME COLUMN `{self.column.name}` TO `{self.column.changeTo}`" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_table.nim new file mode 100644 index 0000000..2c166c6 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/rename_table.nim @@ -0,0 +1,7 @@ +import std/strformat +import ../../models/table +import ./baradb_query_type + +proc renameTable*(self: BaradbSchema, isReset: bool) = + let sql = &"ALTER TABLE `{self.table.name}` RENAME TO `{self.table.changeTo}`" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/reset_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/reset_table.nim new file mode 100644 index 0000000..ce61a8a --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/reset_table.nim @@ -0,0 +1,7 @@ +import std/strformat +import ../../models/table +import ./baradb_query_type + +proc resetTable*(self: BaradbSchema) = + let sql = &"DELETE FROM `{self.table.name}`" + discard waitFor self.rdb.raw(sql).exec() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/schema_utils.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/schema_utils.nim new file mode 100644 index 0000000..88d48b7 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/schema_utils.nim @@ -0,0 +1,12 @@ +import std/strformat +import ../../../query_builder/models/baradb/baradb_types +import ../../models/table + +proc execThenSaveHistory*(rdb: BaradbConnections, name: string, queries: seq[string], checksum: string) = + for sql in queries: + discard waitFor rdb.raw(sql).exec() + let hist = &"INSERT INTO `schema_migrations` (`name`, `checksum`) VALUES ('{name}', '{checksum}')" + discard waitFor rdb.raw(hist).exec() + +proc shouldRun*(rdb: BaradbConnections, table: Table, checksum: string, isReset: bool): bool = + return true diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/add_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/add_column_query.nim new file mode 100644 index 0000000..522eb27 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/add_column_query.nim @@ -0,0 +1,8 @@ +import std/strformat +import ../../models/table +import ../../models/column + +proc addColumnString*(table: Table, column: Column): string = + result = &"`{column.name}` {column.typ}" + if not column.isNullable: + result.add(" NOT NULL") diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/change_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/change_column_query.nim new file mode 100644 index 0000000..0ce0c3b --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/change_column_query.nim @@ -0,0 +1,6 @@ +import std/strformat +import ../../models/table +import ../../models/column + +proc changeColumnString*(table: Table, column: Column): string = + result = &"`{column.name}` {column.typ}" diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/create_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/create_column_query.nim new file mode 100644 index 0000000..39b0fad --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/baradb/sub/create_column_query.nim @@ -0,0 +1,24 @@ +import std/strformat +import ../../models/table +import ../../models/column + +proc createColumnString*(table: Table, column: Column): string = + result = &"`{column.name}` {column.typ}" + if not column.isNullable: + result.add(" NOT NULL") + if column.isUnique: + result.add(" UNIQUE") + if column.isDefault: + result.add(&" DEFAULT {column.default}") + +proc createForeignString*(table: Table, column: Column): string = + result = &"FOREIGN KEY (`{column.name}`) REFERENCES `{column.info}`" + +proc createIndexString*(table: Table, column: Column): string = + result = &"CREATE INDEX `{table.name}_{column.name}_idx` ON `{table.name}` (`{column.name}`)" + +proc createUpdatedAtString*(table: Table, column: Column): string = + result = "" + +proc createCommentColumn*(table: Table, column: Column): string = + result = "" diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/add_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/add_column.nim new file mode 100644 index 0000000..cd412f6 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/add_column.nim @@ -0,0 +1,21 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mariadb_query_type +import ./sub/add_column_query + + +proc addColumn*(self:MariadbSchema, isReset:bool) = + let queries = addColumnString(self.table, self.column) + + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/change_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/change_column.nim new file mode 100644 index 0000000..0480909 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/change_column.nim @@ -0,0 +1,31 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mariadb_query_type +import ./sub/change_column_query + + +proc changeColumn*(self:MariadbSchema, isReset:bool) = + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + var queries:seq[string] + queries.add(&"ALTER TABLE `{self.table.name}` DROP INDEX IF EXISTS `{self.column.name}`") # unique + queries.add(&"ALTER TABLE `{self.table.name}` DROP INDEX IF EXISTS `{self.table.name}_{self.column.name}_index`") + queries.add( + changeColumnString(self.table, self.column) + ) + + if self.column.isIndex: + let indexQuery = addIndexString(self.column, self.table) + queries.add(indexQuery) + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_migration_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_migration_table.nim new file mode 100644 index 0000000..b898dcc --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_migration_table.nim @@ -0,0 +1,38 @@ +import std/strformat +import ../../enums +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mariadb_query_type +import ./sub/create_column_query + + +proc createMigrationTable*(self: MariadbSchema) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(self.table, column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignKey(self.table, column)) + + if column.isIndex: + indexQuery.add(createIndexString(self.table, column)) + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query})" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + exec(self.rdb, queries) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_table.nim new file mode 100644 index 0000000..704c458 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/create_table.nim @@ -0,0 +1,64 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/strutils +import std/sequtils +import std/json +import ../../enums +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mariadb_query_type +import ./sub/create_column_query + + +proc createTable*(self: MariadbSchema, isReset:bool) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(self.table, column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignKey(self.table, column)) + + if column.isIndex: + indexQuery.add(createIndexString(self.table, column)) + + if self.table.primary.len > 0: + let primary = self.table.primary.map( + proc(row:string):string = + return &"`{row}`" + ) + .join(", ") + query.add(&", PRIMARY KEY({primary})") + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query})" + ) + + if self.table.commentContent.len > 0: + queries[^1].add( + &" COMMENT = '{self.table.commentContent}'" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_column.nim new file mode 100644 index 0000000..37d1941 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_column.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mariadb_query_type + + +proc dropColumn*(self:MariadbSchema, isReset:bool) = + let query = &"ALTER TABLE `{self.table.name}` DROP COLUMN `{self.column.name}`" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_table.nim new file mode 100644 index 0000000..7e2a651 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/drop_table.nim @@ -0,0 +1,18 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./schema_utils +import ./mariadb_query_type + + +proc dropTable*(self:MariadbSchema, isReset:bool) = + let query = &"DROP TABLE IF EXISTS `{self.table.name}`" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/mariadb_query_type.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/mariadb_query_type.nim new file mode 100644 index 0000000..a342ff9 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/mariadb_query_type.nim @@ -0,0 +1,15 @@ +import ../../../query_builder/models/mariadb/mariadb_types +import ../../models/table +import ../../models/column + + +type MariadbSchema* = ref object + rdb*:MariadbConnections + table*:Table + column*:Column + +proc new*(_:type MariadbSchema, rdb:MariadbConnections, table:Table):MariadbSchema = + return MariadbSchema(rdb:rdb, table:table) + +proc new*(_:type MariadbSchema, rdb:MariadbConnections, table:Table, column:Column):MariadbSchema = + return MariadbSchema(rdb:rdb, table:table, column:column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_column.nim new file mode 100644 index 0000000..6a24301 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_column.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import std/strformat +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mariadb_query_type + + +proc renameColumn*(self:MariadbSchema, isReset:bool) = + let query = &"ALTER TABLE `{self.table.name}` RENAME COLUMN `{self.column.previousName}` TO `{self.column.name}`" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_table.nim new file mode 100644 index 0000000..d656928 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/rename_table.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./mariadb_query_type +import ./schema_utils + + +proc renameTable*(self:MariadbSchema, isReset:bool) = + let query = &"RENAME TABLE `{self.table.previousName}` TO `{self.table.name}`" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/reset_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/reset_table.nim new file mode 100644 index 0000000..1c457af --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/reset_table.nim @@ -0,0 +1,13 @@ +import std/asyncdispatch +import std/strformat +import ../../../query_builder/models/mariadb/mariadb_query +import ../../../query_builder/models/mariadb/mariadb_exec +import ../../models/table +import ./mariadb_query_type + + +proc resetMigrationTable*(self:MariadbSchema) = + self.rdb.table("_allographer_migrations").where("name", "=", self.table.name).delete.waitFor + +proc resetTable*(self:MariadbSchema) = + self.rdb.raw(&"DROP TABLE IF EXISTS `{self.table.name}`").exec.waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/schema_utils.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/schema_utils.nim new file mode 100644 index 0000000..28ed53e --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/schema_utils.nim @@ -0,0 +1,109 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import std/strutils +import std/times +import ../../../query_builder/models/mariadb/mariadb_types +import ../../../query_builder/models/mariadb/mariadb_query +import ../../../query_builder/models/mariadb/mariadb_exec +import ../../../query_builder/error +import ../../models/table + + +proc notAllowedOption*(option, typ, column:string) = + ## {option} is not allowed in {typ} column {column} + raise newException(DbError, &"{option} is not allowed in {typ} column {column}") + +proc notAllowedType*(typ:string) = + ## Change to {typ} type is not allowed + raise newException(DbError, &"type {typ} is not allowed") + + +# ================================================== +# Mariadb +# ================================================== + +proc shouldRun*(rdb:MariadbConnections, table:Table, checksum:string, isReset:bool):bool = + if isReset: + return true + + let history = rdb.table("_allographer_migrations") + .where("checksum", "=", checksum) + .first() + .waitFor + return not history.isSome() or not history.get()["status"].getBool + + +proc exec*(rdb:MariadbConnections, queries:seq[string]) = + var isSuccess = false + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + + try: + for query in queries: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + +proc execThenSaveHistory*(rdb:MariadbConnections, tableName:string, queries:seq[string], checksum:string) = + var isSuccess = false + try: + for query in queries: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let tableQuery = queries.join("; ") + let createdAt = (proc():string = + if rdb is MariadbConnections: + return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + # if rdb.type == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": tableQuery, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor + + +proc execThenSaveHistory*(rdb:MariadbConnections, tableName:string, query:string, checksum:string) = + var isSuccess = false + try: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let createdAt = (proc():string = + if rdb is MariadbConnections: + return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + # if rdb.driver == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": query, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/add_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/add_column_query.nim new file mode 100644 index 0000000..17a5451 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/add_column_query.nim @@ -0,0 +1,529 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +# ============================================================================= +# int +# ============================================================================= +proc addSerialColumn(column:Column, table:Table):seq[string] = + let query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT" + return @[query] + + +proc addIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` INT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addSmallIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` SMALLINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` MEDIUMINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBigIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BIGINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# float +# ============================================================================= +proc addDecimalColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DECIMAL({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addDoubleColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DOUBLE({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addFloatColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DOUBLE" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# char +# ============================================================================= +proc addCharColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` CHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addStringColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` VARCHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` TEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` MEDIUMTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "medium text", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addLongTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` LONGTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "long text", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# date +# ============================================================================= +proc addDateColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DATE" + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + return @[query] + + +proc addDatetimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` TIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimestampColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimestampsColumn(column:Column, table:Table):seq[string] = + result.add(&"ALTER TABLE `{table.name}` ADD COLUMN `addd_at` DATETIME(3)") + result.add(&"ALTER TABLE `{table.name}` ADD COLUMN `updated_at` DATETIME(3) DEFAULT (NOW())") + +proc addSoftDeleteColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN deleted_at DATETIME(3)" + return @[query] + + +# ============================================================================= +# others +# ============================================================================= +proc addBlobColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BLOB" + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBoolColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BOOLEAN" + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + let defaultInt = if column.defaultBool: 1 else: 0 + query.add(&" DEFAULT {defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addEnumOptions(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(", ") + optionsString.add( + &"'{option}'" + ) + + return optionsString + + +proc addEnumColumn(column:Column, table:Table):seq[string] = + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = addEnumOptions(column.name, options) + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` ENUM({optionsString})" + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + return @[query] + + +proc addJsonColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` JSON" + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultJson.pretty}'") + + return @[query] + + +# ============================================================================= +# foreign key +# ============================================================================= +proc addForeignColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BIGINT" + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + return @[query] + +proc addStrForeignColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` VARCHAR({maxLength})" + if column.isDefault: + query.add(&" DEFAULT {column.defaultString}") + + return @[query] + + +proc addForeignKey(column:Column, table:Table):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"ALTER TABLE `{table.name}` ADD CONSTRAINT `{table.name}_{column.name}_fkey` FOREIGN KEY (`{column.name}`) REFERENCES `{refTable}`(`{refColumn}`) ON DELETE {onDeleteString}" + + +proc addIndexString(column:Column, table:Table):string = + return &"CREATE INDEX IF NOT EXISTS `{table.name}_{column.name}_index` ON `{table.name}`(`{column.name}`)" + + +proc addColumnString*(table:Table, column:Column):seq[string] = + var queries:seq[string] + case column.typ: + # int + of rdbIncrements: + queries = column.addSerialColumn(table) + of rdbInteger: + queries = column.addIntColumn(table) + of rdbSmallInteger: + queries = column.addSmallIntColumn(table) + of rdbMediumInteger: + queries = column.addMediumIntColumn(table) + of rdbBigInteger: + queries = column.addBigIntColumn(table) + # float + of rdbDecimal: + queries = column.addDecimalColumn(table) + of rdbDouble: + queries = column.addDoubleColumn(table) + of rdbFloat: + queries = column.addFloatColumn(table) + # char + of rdbUuid: + queries = column.addStringColumn(table) + of rdbChar: + queries = column.addCharColumn(table) + of rdbString: + queries = column.addStringColumn(table) + # text + of rdbText: + queries = column.addTextColumn(table) + of rdbMediumText: + queries = column.addMediumTextColumn(table) + of rdbLongText: + queries = column.addLongTextColumn(table) + # date + of rdbDate: + queries = column.addDateColumn(table) + of rdbDatetime: + queries = column.addDatetimeColumn(table) + of rdbTime: + queries = column.addTimeColumn(table) + of rdbTimestamp: + queries = column.addTimestampColumn(table) + of rdbTimestamps: + queries = column.addTimestampsColumn(table) + of rdbSoftDelete: + queries = column.addSoftDeleteColumn(table) + # others + of rdbBinary: + queries = column.addBlobColumn(table) + of rdbBoolean: + queries = column.addBoolColumn(table) + of rdbEnumField: + queries = column.addEnumColumn(table) + of rdbJson: + queries = column.addJsonColumn(table) + # foreign + of rdbForeign: + queries = column.addForeignColumn(table) + queries.add(column.addForeignKey(table)) + of rdbStrForeign: + queries = column.addStrForeignColumn(table) + queries.add(column.addForeignKey(table)) + + if column.isIndex: + queries.add(column.addIndexString(table)) + + return queries diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/change_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/change_column_query.nim new file mode 100644 index 0000000..0848213 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/change_column_query.nim @@ -0,0 +1,521 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +# ============================================================================= +# int +# ============================================================================= +# proc addSerialColumn(column:Column, table:Table):seq[string] = +# let query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT" +# return @[query] + + +proc addIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` INT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addSmallIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` SMALLINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` MEDIUMINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBigIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BIGINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# float +# ============================================================================= +proc addDecimalColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DECIMAL({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addDoubleColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DOUBLE({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addFloatColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DOUBLE" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# char +# ============================================================================= +proc addCharColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` CHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addStringColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` VARCHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` TEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` MEDIUMTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "medium text", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addLongTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` LONGTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "long text", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# date +# ============================================================================= +proc addDateColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DATE" + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + return @[query] + + +proc addDatetimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` TIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimestampColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# proc addTimestampsColumn(column:Column, table:Table):seq[string] = +# result.add(&"ALTER TABLE `{table.name}` MODIFY COLUMN `addd_at` DATETIME(3)") +# result.add(&"ALTER TABLE `{table.name}` MODIFY COLUMN `updated_at` DATETIME(3) DEFAULT (NOW())") + +# proc addSoftDeleteColumn(column:Column, table:Table):seq[string] = +# var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN deleted_at DATETIME(3)" +# return @[query] + + +# ============================================================================= +# others +# ============================================================================= +proc addBlobColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BLOB" + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBoolColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BOOLEAN" + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + let defaultInt = if column.defaultBool: 1 else: 0 + query.add(&" DEFAULT {defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addEnumOptions(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(", ") + optionsString.add( + &"'{option}'" + ) + + return optionsString + + +proc addEnumColumn(column:Column, table:Table):seq[string] = + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = addEnumOptions(column.name, options) + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` ENUM({optionsString})" + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + return @[query] + + +proc addJsonColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` JSON" + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultJson.pretty}'") + + return @[query] + + +# ============================================================================= +# foreign key +# ============================================================================= +# proc addForeignColumn(column:Column, table:Table):seq[string] = +# var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BIGINT" +# if column.isDefault: +# query.add(&" DEFAULT {column.defaultInt}") + +# return @[query] + +# proc addStrForeignColumn(column:Column, table:Table):seq[string] = +# let maxLength = column.info["maxLength"].getInt +# var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` VARCHAR({maxLength})" +# if column.isDefault: +# query.add(&" DEFAULT {column.defaultString}") + +# return @[query] + + +proc changeForeignKey*(column:Column, table:Table):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"ALTER TABLE `{table.name}` ADD CONSTRAINT `{table.name}_{column.name}_fkey` FOREIGN KEY (`{column.name}`) REFERENCES `{refTable}`(`{refColumn}`) ON DELETE {onDeleteString}" + + +proc addIndexString*(column:Column, table:Table):string = + return &"CREATE INDEX IF NOT EXISTS `{table.name}_{column.name}_index` ON `{table.name}`(`{column.name}`)" + + +proc changeColumnString*(table:Table, column:Column):seq[string] = + case column.typ: + # int + of rdbIncrements: + notAllowedType("increments") + of rdbInteger: + return column.addIntColumn(table) + of rdbSmallInteger: + return column.addSmallIntColumn(table) + of rdbMediumInteger: + return column.addMediumIntColumn(table) + of rdbBigInteger: + return column.addBigIntColumn(table) + # float + of rdbDecimal: + return column.addDecimalColumn(table) + of rdbDouble: + return column.addDoubleColumn(table) + of rdbFloat: + return column.addFloatColumn(table) + # char + of rdbUuid: + return column.addStringColumn(table) + of rdbChar: + return column.addCharColumn(table) + of rdbString: + return column.addStringColumn(table) + # text + of rdbText: + return column.addTextColumn(table) + of rdbMediumText: + return column.addMediumTextColumn(table) + of rdbLongText: + return column.addLongTextColumn(table) + # date + of rdbDate: + return column.addDateColumn(table) + of rdbDatetime: + return column.addDatetimeColumn(table) + of rdbTime: + return column.addTimeColumn(table) + of rdbTimestamp: + return column.addTimestampColumn(table) + of rdbTimestamps: + notAllowedType("timestamps") + of rdbSoftDelete: + notAllowedType("softDelete") + # others + of rdbBinary: + return column.addBlobColumn(table) + of rdbBoolean: + return column.addBoolColumn(table) + of rdbEnumField: + return column.addEnumColumn(table) + of rdbJson: + return column.addJsonColumn(table) + # foreign + of rdbForeign: + notAllowedType("foreign") + of rdbStrForeign: + notAllowedType("strForeign") diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/create_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/create_column_query.nim new file mode 100644 index 0000000..acde7c5 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mariadb/sub/create_column_query.nim @@ -0,0 +1,600 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +# ============================================================================= +# int +# ============================================================================= +proc createSerialColumn(column:Column):string = + result = &"`{column.name}` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT" + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createIntColumn(column:Column):string = + result = &"`{column.name}` INT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createSmallIntColumn(column:Column):string = + result = &"`{column.name}` SMALLINT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createMediumIntColumn(column:Column):string = + result = &"`{column.name}` MEDIUMINT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createBigIntColumn(column:Column):string = + result = &"`{column.name}` BIGINT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# float +# ============================================================================= +proc createDecimalColumn(column:Column):string = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + result = &"`{column.name}` DECIMAL({maximum}, {digit})" + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createDoubleColumn(column:Column):string = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + result = &"`{column.name}` DOUBLE({maximum}, {digit})" + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createFloatColumn(column:Column):string = + result = &"`{column.name}` DOUBLE" + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# char +# ============================================================================= +proc createCharColumn(column:Column):string = + let maxLength = column.info["maxLength"].getInt + result = &"`{column.name}` CHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createStringColumn(column:Column):string = + let maxLength = column.info["maxLength"].getInt + result = &"`{column.name}` VARCHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTextColumn(column:Column):string = + result = &"`{column.name}` TEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + if column.isUnique: + # notAllowedOption("unique", "text", column.name) + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createMediumTextColumn(column:Column):string = + result = &"`{column.name}` MEDIUMTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "medium text", column.name) + + if column.isUnique: + result.add(" UNIQUE") + # notAllowedOption("unique", "medium text", column.name) + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createLongTextColumn(column:Column):string = + result = &"`{column.name}` LONGTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "long text", column.name) + + if column.isUnique: + result.add(" UNIQUE") + # notAllowedOption("unique", "long text", column.name) + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# date +# ============================================================================= +proc createDateColumn(column:Column):string = + result = &"`{column.name}` DATE" + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createDatetimeColumn(column:Column):string = + # result = &"`{column.name}` DATETIME(3)" + result = &"`{column.name}` DATETIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTimeColumn(column:Column):string = + result = &"`{column.name}` TIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTimestampColumn(column:Column):string = + result = &"`{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTimestampsColumn(column:Column):string = + result = "`created_at` DATETIME(3)" + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + result.add(", ") + + result.add("`updated_at` DATETIME(3) DEFAULT (NOW())") + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createSoftDeleteColumn(column:Column):string = + result = "`deleted_at` DATETIME(3)" + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# others +# ============================================================================= +proc createBlobColumn(column:Column):string = + result = &"`{column.name}` BLOB" + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + if column.isUnique: + result.add(" UNIQUE") + # notAllowedOption("unique", "blob", column.name) + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createBoolColumn(column:Column):string = + result = &"`{column.name}` BOOLEAN" + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + let defaultInt = if column.defaultBool: 1 else: 0 + result.add(&" DEFAULT {defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createEnumOptions(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(", ") + optionsString.add( + &"'{option}'" + ) + + return optionsString + +proc createEnumColumn(column:Column):string = + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = createEnumOptions(column.name, options) + result = &"`{column.name}` ENUM({optionsString})" + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createJsonColumn(column:Column):string = + result = &"`{column.name}` JSON" + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultJson.pretty}'") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# foreign key +# ============================================================================= +proc createForeignColumn(column:Column):string = + result = &"`{column.name}` BIGINT" + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createStrForeignColumn(column:Column):string = + let maxLength = column.info["maxLength"].getInt + result = &"`{column.name}` VARCHAR({maxLength})" + + if column.isDefault: + result.add(&" DEFAULT {column.defaultString}") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createForeignKey*(table:Table, column:Column):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"FOREIGN KEY `{table.name}_{column.name}_fkey` (`{column.name}`) REFERENCES `{refTable}`(`{refColumn}`) ON DELETE {onDeleteString}" + +proc alterAddForeignKey*(column:Column, table:Table):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let tableName = table.name + let constraintName = &"{tableName}_{column.name}" + let refTable = column.info["table"].getStr + let refColumn = column.info["column"].getStr + return &"CONSTRAINT `{constraintName}` FOREIGN KEY (`{column.name}`) REFERENCES `{refTable}`({refColumn}) ON DELETE {onDeleteString}" + +# proc alterDeleteColumn*(column:Column, table:Table):string = +# var table = table.name +# return &"ALTER TABLE `{table}` DROP `{column.name}`" + +# proc alterDeleteForeignColumn*(column:Column, table:Table):string = +# var tableName = table.name +# var constraintName = &"{tableName}_{column.name}" +# return &"ALTER TABLE `{table.name}` DROP FOREIGN KEY `{constraintName}`" + +proc createIndexString*(table:Table, column:Column):string = + return &"CREATE INDEX IF NOT EXISTS `{table.name}_{column.name}_index` ON `{table.name}`(`{column.name}`)" + + +proc createColumnString*(table:Table, column:Column):string = + case column.typ: + # int + of rdbIncrements: + return column.createSerialColumn() + of rdbInteger: + return column.createIntColumn() + of rdbSmallInteger: + return column.createSmallIntColumn() + of rdbMediumInteger: + return column.createMediumIntColumn() + of rdbBigInteger: + return column.createBigIntColumn() + # float + of rdbDecimal: + return column.createDecimalColumn() + of rdbDouble: + # return column.createDecimalColumn() + return column.createDoubleColumn() + of rdbFloat: + return column.createFloatColumn() + # char + of rdbUuid: + return column.createStringColumn() + of rdbChar: + return column.createCharColumn() + of rdbString: + return column.createStringColumn() + # text + of rdbText: + return column.createTextColumn() + of rdbMediumText: + return column.createMediumTextColumn() + of rdbLongText: + return column.createLongTextColumn() + # date + of rdbDate: + return column.createDateColumn() + of rdbDatetime: + return column.createDatetimeColumn() + of rdbTime: + return column.createTimeColumn() + of rdbTimestamp: + return column.createTimestampColumn() + of rdbTimestamps: + return column.createTimestampsColumn() + of rdbSoftDelete: + return column.createSoftDeleteColumn() + # others + of rdbBinary: + return column.createBlobColumn() + of rdbBoolean: + return column.createBoolColumn() + of rdbEnumField: + return column.createEnumColumn() + of rdbJson: + return column.createJsonColumn() + # foreign + of rdbForeign: + return column.createForeignColumn() + of rdbStrForeign: + return column.createStrForeignColumn() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/add_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/add_column.nim new file mode 100644 index 0000000..994dcb7 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/add_column.nim @@ -0,0 +1,21 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mysql_query_type +import ./sub/add_column_query + + +proc addColumn*(self:MysqlSchema, isReset:bool) = + let queries = addColumnString(self.table, self.column) + + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/change_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/change_column.nim new file mode 100644 index 0000000..a54b2e9 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/change_column.nim @@ -0,0 +1,35 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/asyncdispatch +import std/json +import std/strformat +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mysql_query_type +import ./sub/change_column_query +import ./sub/is_exists + + +proc changeColumn*(self:MysqlSchema, isReset:bool) = + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + var queries:seq[string] + if isExistsUnique(self.rdb, self.table, self.column).waitFor(): + queries.add(&"ALTER TABLE `{self.table.name}` DROP INDEX `{self.column.name}`") # unique + if isExistsIndex(self.rdb, self.table, self.column).waitFor(): + queries.add(&"ALTER TABLE `{self.table.name}` DROP INDEX `{self.table.name}_{self.column.name}_index`") + queries.add( + changeColumnString(self.table, self.column) + ) + + if self.column.isIndex: + let indexQuery = addIndexString(self.column, self.table) + queries.add(indexQuery) + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_migration_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_migration_table.nim new file mode 100644 index 0000000..9525edc --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_migration_table.nim @@ -0,0 +1,52 @@ +import std/asyncdispatch +import std/strformat +# import std/options +# import std/json +# import ../../../query_builder +import ../../enums +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mysql_query_type +import ./sub/create_column_query +import ./sub/is_exists + + +proc createMigrationTable*(self: MysqlSchema) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(self.table, column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignKey(self.table, column)) + + if column.isIndex: + let logDisplay = self.rdb.log.shouldDisplayLog + let logFile = self.rdb.log.shouldOutputLogFile + self.rdb.log.shouldDisplayLog = false + self.rdb.log.shouldOutputLogFile = false + defer: + self.rdb.log.shouldDisplayLog = logDisplay + self.rdb.log.shouldOutputLogFile = logFile + + if not isExistsIndex(self.rdb, self.table, column).waitFor(): + indexQuery.add(createIndexString(self.table, column)) + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query})" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + exec(self.rdb, queries) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_table.nim new file mode 100644 index 0000000..2be12f5 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/create_table.nim @@ -0,0 +1,78 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/asyncdispatch +import std/strformat +import std/strutils +import std/sequtils +import std/json +import ../../../query_builder +import ../../enums +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mysql_query_type +import ./sub/create_column_query +import ./sub/is_exists + + +proc createTable*(self: MysqlSchema, isReset:bool) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(self.table, column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignKey(self.table, column)) + + if column.isIndex: + let logDisplay = self.rdb.log.shouldDisplayLog + let logFile = self.rdb.log.shouldOutputLogFile + self.rdb.log.shouldDisplayLog = false + self.rdb.log.shouldOutputLogFile = false + defer: + self.rdb.log.shouldDisplayLog = logDisplay + self.rdb.log.shouldOutputLogFile = logFile + + if not isExistsIndex(self.rdb, self.table, column).waitFor(): + if [rdbText, rdbMediumText,rdbLongText, rdbBinary, rdbJson].contains(column.typ): + dbError("BLOB, TEXT and JSON column can't use index") + indexQuery.add(createIndexString(self.table, column)) + + if self.table.primary.len > 0: + let primary = self.table.primary.map( + proc(row:string):string = + return &"`{row}`" + ) + .join(", ") + query.add(&", PRIMARY KEY({primary})") + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS `{self.table.name}` ({query})" + ) + + if self.table.commentContent.len > 0: + queries[^1].add( + &" COMMENT = '{self.table.commentContent}'" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_column.nim new file mode 100644 index 0000000..5c7883c --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_column.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mysql_query_type + + +proc dropColumn*(self:MysqlSchema, isReset:bool) = + let query = &"ALTER TABLE `{self.table.name}` DROP COLUMN `{self.column.name}`" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_table.nim new file mode 100644 index 0000000..c616967 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/drop_table.nim @@ -0,0 +1,18 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./schema_utils +import ./mysql_query_type + + +proc dropTable*(self:MysqlSchema, isReset:bool) = + let query = &"DROP TABLE IF EXISTS `{self.table.name}`" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/mysql_query_type.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/mysql_query_type.nim new file mode 100644 index 0000000..dfc3f88 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/mysql_query_type.nim @@ -0,0 +1,15 @@ +import ../../../query_builder/models/mysql/mysql_types +import ../../models/table +import ../../models/column + + +type MysqlSchema* = ref object + rdb*:MysqlConnections + table*:Table + column*:Column + +proc new*(_:type MysqlSchema, rdb:MysqlConnections, table:Table):MysqlSchema = + return MysqlSchema(rdb:rdb, table:table) + +proc new*(_:type MysqlSchema, rdb:MysqlConnections, table:Table, column:Column):MysqlSchema = + return MysqlSchema(rdb:rdb, table:table, column:column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_column.nim new file mode 100644 index 0000000..98f1672 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_column.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import std/strformat +import ../../models/table +import ../../models/column +import ./schema_utils +import ./mysql_query_type + + +proc renameColumn*(self:MysqlSchema, isReset:bool) = + let query = &"ALTER TABLE `{self.table.name}` RENAME COLUMN `{self.column.previousName}` TO `{self.column.name}`" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_table.nim new file mode 100644 index 0000000..3fcf39e --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/rename_table.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./mysql_query_type +import ./schema_utils + + +proc renameTable*(self:MysqlSchema, isReset:bool) = + let query = &"RENAME TABLE `{self.table.previousName}` TO `{self.table.name}`" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/reset_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/reset_table.nim new file mode 100644 index 0000000..90e65fb --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/reset_table.nim @@ -0,0 +1,13 @@ +import std/asyncdispatch +import std/strformat +import ../../../query_builder/models/mysql/mysql_query +import ../../../query_builder/models/mysql/mysql_exec +import ../../models/table +import ./mysql_query_type + + +proc resetMigrationTable*(self:MysqlSchema) = + self.rdb.table("_allographer_migrations").where("name", "=", self.table.name).delete().waitFor + +proc resetTable*(self:MysqlSchema) = + self.rdb.raw(&"DROP TABLE IF EXISTS `{self.table.name}`").exec().waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/schema_utils.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/schema_utils.nim new file mode 100644 index 0000000..3c240f7 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/schema_utils.nim @@ -0,0 +1,109 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import std/strutils +import std/times +import ../../../query_builder/models/mysql/mysql_types +import ../../../query_builder/models/mysql/mysql_query +import ../../../query_builder/models/mysql/mysql_exec +import ../../../query_builder/error +import ../../models/table + + +proc notAllowedOption*(option, typ, column:string) = + ## {option} is not allowed in {typ} column {column} + raise newException(DbError, &"{option} is not allowed in {typ} column {column}") + +proc notAllowedType*(typ:string) = + ## Change to {typ} type is not allowed + raise newException(DbError, &"type {typ} is not allowed") + + +# ================================================== +# Mysql +# ================================================== + +proc shouldRun*(rdb:MysqlConnections, table:Table, checksum:string, isReset:bool):bool = + if isReset: + return true + + let history = rdb.table("_allographer_migrations") + .where("checksum", "=", checksum) + .first() + .waitFor + return not history.isSome() or not history.get()["status"].getBool + + +proc exec*(rdb:MysqlConnections, queries:seq[string]) = + var isSuccess = false + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + + try: + for query in queries: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + +proc execThenSaveHistory*(rdb:MysqlConnections, tableName:string, queries:seq[string], checksum:string) = + var isSuccess = false + try: + for query in queries: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let tableQuery = queries.join("; ") + let createdAt = (proc():string = + if rdb is MysqlConnections: + return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + # if rdb.type == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": tableQuery, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor + + +proc execThenSaveHistory*(rdb:MysqlConnections, tableName:string, query:string, checksum:string) = + var isSuccess = false + try: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let createdAt = (proc():string = + if rdb is MysqlConnections: + return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + # if rdb.driver == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": query, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/add_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/add_column_query.nim new file mode 100644 index 0000000..d0acf33 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/add_column_query.nim @@ -0,0 +1,546 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +## In MySQL, BLOB, TEXT, GEOMETRY or JSON column can't have a default value. + +# ============================================================================= +# int +# ============================================================================= +proc addSerialColumn(column:Column, table:Table):seq[string] = + let query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT" + return @[query] + + +proc addIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` INT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addSmallIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` SMALLINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` MEDIUMINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBigIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BIGINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# float +# ============================================================================= +proc addDecimalColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DECIMAL({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addDoubleColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DOUBLE({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addFloatColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DOUBLE" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# char +# ============================================================================= +proc addCharColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` CHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addStringColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` VARCHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` TEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + if column.isUnique: + notAllowedOption("unique", "text", column.name) + + if column.isDefault: + notAllowedOption("default", "text", column.name) + + if column.isIndex: + notAllowedOption("index", "text", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` MEDIUMTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "medium text", column.name) + + if column.isUnique: + notAllowedOption("unique", "medium text", column.name) + + if column.isDefault: + notAllowedOption("default", "medium text", column.name) + + if column.isIndex: + notAllowedOption("index", "medium text", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addLongTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` LONGTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "long text", column.name) + + if column.isUnique: + notAllowedOption("unique", "long text", column.name) + + if column.isDefault: + notAllowedOption("default", "long text", column.name) + + if column.isIndex: + notAllowedOption("index", "long text", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# date +# ============================================================================= +proc addDateColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DATE" + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + return @[query] + + +proc addDatetimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` TIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimestampColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimestampsColumn(column:Column, table:Table):seq[string] = + result.add(&"ALTER TABLE `{table.name}` ADD COLUMN `addd_at` DATETIME(3)") + result.add(&"ALTER TABLE `{table.name}` ADD COLUMN `updated_at` DATETIME(3) DEFAULT (NOW())") + +proc addSoftDeleteColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN deleted_at DATETIME(3)" + return @[query] + + +# ============================================================================= +# others +# ============================================================================= +proc addBlobColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BLOB" + + if column.isUnsigned: + notAllowedOption("unsigned", "binary", column.name) + + if column.isUnique: + notAllowedOption("unique", "binary", column.name) + + if column.isDefault: + notAllowedOption("default", "binary", column.name) + + if column.isIndex: + notAllowedOption("index", "binary", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBoolColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BOOLEAN" + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + let defaultInt = if column.defaultBool: 1 else: 0 + query.add(&" DEFAULT {defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addEnumOptions(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(", ") + optionsString.add( + &"'{option}'" + ) + + return optionsString + + +proc addEnumColumn(column:Column, table:Table):seq[string] = + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = addEnumOptions(column.name, options) + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` ENUM({optionsString})" + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + return @[query] + + +proc addJsonColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` JSON" + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + if column.isUnique: + notAllowedOption("unique", "json", column.name) + + if column.isDefault: + notAllowedOption("default", "json", column.name) + + if column.isIndex: + notAllowedOption("index", "json", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# foreign key +# ============================================================================= +proc addForeignColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` BIGINT" + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + return @[query] + +proc addStrForeignColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` ADD COLUMN `{column.name}` VARCHAR({maxLength})" + if column.isDefault: + query.add(&" DEFAULT {column.defaultString}") + + return @[query] + + +proc addForeignKey(column:Column, table:Table):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"ALTER TABLE `{table.name}` ADD CONSTRAINT `{table.name}_{column.name}_fkey` FOREIGN KEY (`{column.name}`) REFERENCES `{refTable}`(`{refColumn}`) ON DELETE {onDeleteString}" + + +proc addIndexString(column:Column, table:Table):string = + return &"CREATE INDEX `{table.name}_{column.name}_index` ON `{table.name}`(`{column.name}`)" + + +proc addColumnString*(table:Table, column:Column):seq[string] = + var queries:seq[string] + case column.typ: + # int + of rdbIncrements: + queries = column.addSerialColumn(table) + of rdbInteger: + queries = column.addIntColumn(table) + of rdbSmallInteger: + queries = column.addSmallIntColumn(table) + of rdbMediumInteger: + queries = column.addMediumIntColumn(table) + of rdbBigInteger: + queries = column.addBigIntColumn(table) + # float + of rdbDecimal: + queries = column.addDecimalColumn(table) + of rdbDouble: + queries = column.addDoubleColumn(table) + of rdbFloat: + queries = column.addFloatColumn(table) + # char + of rdbUuid: + queries = column.addStringColumn(table) + of rdbChar: + queries = column.addCharColumn(table) + of rdbString: + queries = column.addStringColumn(table) + # text + of rdbText: + queries = column.addTextColumn(table) + of rdbMediumText: + queries = column.addMediumTextColumn(table) + of rdbLongText: + queries = column.addLongTextColumn(table) + # date + of rdbDate: + queries = column.addDateColumn(table) + of rdbDatetime: + queries = column.addDatetimeColumn(table) + of rdbTime: + queries = column.addTimeColumn(table) + of rdbTimestamp: + queries = column.addTimestampColumn(table) + of rdbTimestamps: + queries = column.addTimestampsColumn(table) + of rdbSoftDelete: + queries = column.addSoftDeleteColumn(table) + # others + of rdbBinary: + queries = column.addBlobColumn(table) + of rdbBoolean: + queries = column.addBoolColumn(table) + of rdbEnumField: + queries = column.addEnumColumn(table) + of rdbJson: + queries = column.addJsonColumn(table) + # foreign + of rdbForeign: + queries = column.addForeignColumn(table) + queries.add(column.addForeignKey(table)) + of rdbStrForeign: + queries = column.addStrForeignColumn(table) + queries.add(column.addForeignKey(table)) + + if column.isIndex: + queries.add(column.addIndexString(table)) + + return queries diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/change_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/change_column_query.nim new file mode 100644 index 0000000..a5eb25c --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/change_column_query.nim @@ -0,0 +1,536 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +# ============================================================================= +# int +# ============================================================================= +# proc addSerialColumn(column:Column, table:Table):seq[string] = +# let query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT" +# return @[query] + + +proc addIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` INT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addSmallIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` SMALLINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` MEDIUMINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBigIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BIGINT" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# float +# ============================================================================= +proc addDecimalColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DECIMAL({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addDoubleColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DOUBLE({maximum}, {digit})" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addFloatColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DOUBLE" + + if column.isUnsigned: + query.add(" UNSIGNED") + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# char +# ============================================================================= +proc addCharColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` CHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addStringColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` VARCHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` TEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + if column.isUnique: + notAllowedOption("unique", "text", column.name) + + if column.isDefault: + notAllowedOption("default", "text", column.name) + + if column.isIndex: + notAllowedOption("index", "text", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addMediumTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` MEDIUMTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "medium text", column.name) + + if column.isUnique: + notAllowedOption("unique", "medium text", column.name) + + if column.isDefault: + notAllowedOption("default", "medium text", column.name) + + if column.isIndex: + notAllowedOption("index", "medium text", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addLongTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` LONGTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "long text", column.name) + + if column.isUnique: + notAllowedOption("unique", "long text", column.name) + + if column.isDefault: + notAllowedOption("default", "long text", column.name) + + if column.isIndex: + notAllowedOption("index", "long text", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# date +# ============================================================================= +proc addDateColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DATE" + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + return @[query] + + +proc addDatetimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` TIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addTimestampColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# proc addTimestampsColumn(column:Column, table:Table):seq[string] = +# result.add(&"ALTER TABLE `{table.name}` MODIFY COLUMN `addd_at` DATETIME(3)") +# result.add(&"ALTER TABLE `{table.name}` MODIFY COLUMN `updated_at` DATETIME(3) DEFAULT (NOW())") + +# proc addSoftDeleteColumn(column:Column, table:Table):seq[string] = +# var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN deleted_at DATETIME(3)" +# return @[query] + + +# ============================================================================= +# others +# ============================================================================= +proc addBlobColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BLOB" + + if column.isUnsigned: + notAllowedOption("unsigned", "binary", column.name) + + if column.isUnique: + notAllowedOption("unique", "binary", column.name) + + if column.isDefault: + notAllowedOption("default", "binary", column.name) + + if column.isIndex: + notAllowedOption("index", "binary", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addBoolColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BOOLEAN" + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if column.isDefault: + let defaultInt = if column.defaultBool: 1 else: 0 + query.add(&" DEFAULT {defaultInt}") + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +proc addEnumOptions(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(", ") + optionsString.add( + &"'{option}'" + ) + + return optionsString + + +proc addEnumColumn(column:Column, table:Table):seq[string] = + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = addEnumOptions(column.name, options) + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` ENUM({optionsString})" + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + if column.isUnique: + query.add(" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + return @[query] + + +proc addJsonColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` JSON" + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + if column.isUnique: + notAllowedOption("unique", "json", column.name) + + if column.isDefault: + notAllowedOption("default", "json", column.name) + + if column.isIndex: + notAllowedOption("index", "json", column.name) + + if not column.isNullable: + query.add(" NOT NULL") + + return @[query] + + +# ============================================================================= +# foreign key +# ============================================================================= +# proc addForeignColumn(column:Column, table:Table):seq[string] = +# var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` BIGINT" +# if column.isDefault: +# query.add(&" DEFAULT {column.defaultInt}") + +# return @[query] + +# proc addStrForeignColumn(column:Column, table:Table):seq[string] = +# let maxLength = column.info["maxLength"].getInt +# var query = &"ALTER TABLE `{table.name}` MODIFY COLUMN `{column.name}` VARCHAR({maxLength})" +# if column.isDefault: +# query.add(&" DEFAULT {column.defaultString}") + +# return @[query] + + +proc changeForeignKey*(column:Column, table:Table):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"ALTER TABLE `{table.name}` ADD CONSTRAINT `{table.name}_{column.name}_fkey` FOREIGN KEY (`{column.name}`) REFERENCES `{refTable}`(`{refColumn}`) ON DELETE {onDeleteString}" + + +proc addIndexString*(column:Column, table:Table):string = + return &"CREATE INDEX `{table.name}_{column.name}_index` ON `{table.name}`(`{column.name}`)" + + +proc changeColumnString*(table:Table, column:Column):seq[string] = + case column.typ: + # int + of rdbIncrements: + notAllowedType("increments") + of rdbInteger: + return column.addIntColumn(table) + of rdbSmallInteger: + return column.addSmallIntColumn(table) + of rdbMediumInteger: + return column.addMediumIntColumn(table) + of rdbBigInteger: + return column.addBigIntColumn(table) + # float + of rdbDecimal: + return column.addDecimalColumn(table) + of rdbDouble: + return column.addDoubleColumn(table) + of rdbFloat: + return column.addFloatColumn(table) + # char + of rdbUuid: + return column.addStringColumn(table) + of rdbChar: + return column.addCharColumn(table) + of rdbString: + return column.addStringColumn(table) + # text + of rdbText: + return column.addTextColumn(table) + of rdbMediumText: + return column.addMediumTextColumn(table) + of rdbLongText: + return column.addLongTextColumn(table) + # date + of rdbDate: + return column.addDateColumn(table) + of rdbDatetime: + return column.addDatetimeColumn(table) + of rdbTime: + return column.addTimeColumn(table) + of rdbTimestamp: + return column.addTimestampColumn(table) + of rdbTimestamps: + notAllowedType("timestamps") + of rdbSoftDelete: + notAllowedType("softDelete") + # others + of rdbBinary: + return column.addBlobColumn(table) + of rdbBoolean: + return column.addBoolColumn(table) + of rdbEnumField: + return column.addEnumColumn(table) + of rdbJson: + return column.addJsonColumn(table) + # foreign + of rdbForeign: + notAllowedType("foreign") + of rdbStrForeign: + notAllowedType("strForeign") diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/create_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/create_column_query.nim new file mode 100644 index 0000000..4b54626 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/create_column_query.nim @@ -0,0 +1,610 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +## In MySQL, BLOB, TEXT, GEOMETRY or JSON column can't have a default value. +## BLOB/TEXT column caughn't use in key specification without a key length. + +# ============================================================================= +# int +# ============================================================================= +proc createSerialColumn(column:Column):string = + result = &"`{column.name}` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT" + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createIntColumn(column:Column):string = + result = &"`{column.name}` INT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createSmallIntColumn(column:Column):string = + result = &"`{column.name}` SMALLINT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createMediumIntColumn(column:Column):string = + result = &"`{column.name}` MEDIUMINT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createBigIntColumn(column:Column):string = + result = &"`{column.name}` BIGINT" + + if column.isAutoIncrement: + result.add(" AUTO_INCREMENT") + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# float +# ============================================================================= +proc createDecimalColumn(column:Column):string = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + result = &"`{column.name}` DECIMAL({maximum}, {digit})" + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createDoubleColumn(column:Column):string = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + result = &"`{column.name}` DOUBLE({maximum}, {digit})" + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createFloatColumn(column:Column):string = + result = &"`{column.name}` DOUBLE" + + if column.isUnsigned: + result.add(" UNSIGNED") + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# char +# ============================================================================= +proc createCharColumn(column:Column):string = + let maxLength = column.info["maxLength"].getInt + result = &"`{column.name}` CHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createStringColumn(column:Column):string = + let maxLength = column.info["maxLength"].getInt + result = &"`{column.name}` VARCHAR({maxLength})" + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTextColumn(column:Column):string = + result = &"`{column.name}` TEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + if column.isUnique: + notAllowedOption("unique", "text", column.name) + + if column.isDefault: + notAllowedOption("default", "text", column.name) + + if column.isIndex: + notAllowedOption("index", "text", column.name) + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createMediumTextColumn(column:Column):string = + result = &"`{column.name}` MEDIUMTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "medium text", column.name) + + if column.isUnique: + notAllowedOption("unique", "medium text", column.name) + + if column.isDefault: + notAllowedOption("default", "text", column.name) + + if column.isIndex: + notAllowedOption("index", "text", column.name) + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createLongTextColumn(column:Column):string = + result = &"`{column.name}` LONGTEXT" + + if column.isUnsigned: + notAllowedOption("unsigned", "long text", column.name) + + if column.isUnique: + notAllowedOption("unique", "long text", column.name) + + if column.isDefault: + notAllowedOption("default", "text", column.name) + + if column.isIndex: + notAllowedOption("index", "text", column.name) + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# date +# ============================================================================= +proc createDateColumn(column:Column):string = + result = &"`{column.name}` DATE" + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createDatetimeColumn(column:Column):string = + # result = &"`{column.name}` DATETIME(3)" + result = &"`{column.name}` DATETIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTimeColumn(column:Column):string = + result = &"`{column.name}` TIME" + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTimestampColumn(column:Column):string = + result = &"`{column.name}` DATETIME(3)" + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createTimestampsColumn(column:Column):string = + result = "`created_at` DATETIME(3)" + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + result.add(", ") + + result.add("`updated_at` DATETIME(3) DEFAULT (NOW())") + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createSoftDeleteColumn(column:Column):string = + result = "`deleted_at` DATETIME(3)" + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# others +# ============================================================================= +proc createBlobColumn(column:Column):string = + result = &"`{column.name}` BLOB" + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + if column.isUnique: + notAllowedOption("unique", "blob", column.name) + + if column.isDefault: + notAllowedOption("default", "binary", column.name) + + if column.isIndex: + notAllowedOption("index", "binary", column.name) + + if not column.isNullable: + result.add(" NOT NULL") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createBoolColumn(column:Column):string = + result = &"`{column.name}` BOOLEAN" + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if column.isDefault: + let defaultInt = if column.defaultBool: 1 else: 0 + result.add(&" DEFAULT {defaultInt}") + + if not column.isNullable: + result.add(" NOT NULL") + + +proc createEnumOptions(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(", ") + optionsString.add( + &"'{option}'" + ) + + return optionsString + +proc createEnumColumn(column:Column):string = + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = createEnumOptions(column.name, options) + result = &"`{column.name}` ENUM({optionsString})" + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createJsonColumn(column:Column):string = + result = &"`{column.name}` JSON" + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + if column.isUnique: + notAllowedOption("unique", "json", column.name) + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + notAllowedOption("default", "json", column.name) + + if column.isIndex: + notAllowedOption("index", "json", column.name) + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +# ============================================================================= +# foreign key +# ============================================================================= +proc createForeignColumn(column:Column):string = + result = &"`{column.name}` BIGINT" + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + +proc createStrForeignColumn(column:Column):string = + let maxLength = column.info["maxLength"].getInt + result = &"`{column.name}` VARCHAR({maxLength})" + + if column.isDefault: + result.add(&" DEFAULT {column.defaultString}") + + if column.commentContent.len > 0: + result.add(&" COMMENT '{column.commentContent}'") + + +proc createForeignKey*(table:Table, column:Column):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"FOREIGN KEY `{table.name}_{column.name}_fkey` (`{column.name}`) REFERENCES `{refTable}`(`{refColumn}`) ON DELETE {onDeleteString}" + +proc alterAddForeignKey*(column:Column, table:Table):string = + let onDeleteString = + case column.foreignOnDelete + of RESTRICT: + "RESTRICT" + of CASCADE: + "CASCADE" + of SET_NULL: + "SET NULL" + of NO_ACTION: + "NO ACTION" + + let tableName = table.name + let constraintName = &"{tableName}_{column.name}" + let refTable = column.info["table"].getStr + let refColumn = column.info["column"].getStr + return &"CONSTRAINT `{constraintName}` FOREIGN KEY (`{column.name}`) REFERENCES `{refTable}`({refColumn}) ON DELETE {onDeleteString}" + +# proc alterDeleteColumn*(column:Column, table:Table):string = +# var table = table.name +# return &"ALTER TABLE `{table}` DROP `{column.name}`" + +# proc alterDeleteForeignColumn*(column:Column, table:Table):string = +# var tableName = table.name +# var constraintName = &"{tableName}_{column.name}" +# return &"ALTER TABLE `{table.name}` DROP FOREIGN KEY `{constraintName}`" + +proc createIndexString*(table:Table, column:Column):string = + return &"CREATE INDEX `{table.name}_{column.name}_index` ON `{table.name}`(`{column.name}`)" + + +proc createColumnString*(table:Table, column:Column):string = + case column.typ: + # int + of rdbIncrements: + return column.createSerialColumn() + of rdbInteger: + return column.createIntColumn() + of rdbSmallInteger: + return column.createSmallIntColumn() + of rdbMediumInteger: + return column.createMediumIntColumn() + of rdbBigInteger: + return column.createBigIntColumn() + # float + of rdbDecimal: + return column.createDecimalColumn() + of rdbDouble: + # return column.createDecimalColumn() + return column.createDoubleColumn() + of rdbFloat: + return column.createFloatColumn() + # char + of rdbUuid: + return column.createStringColumn() + of rdbChar: + return column.createCharColumn() + of rdbString: + return column.createStringColumn() + # text + of rdbText: + return column.createTextColumn() + of rdbMediumText: + return column.createMediumTextColumn() + of rdbLongText: + return column.createLongTextColumn() + # date + of rdbDate: + return column.createDateColumn() + of rdbDatetime: + return column.createDatetimeColumn() + of rdbTime: + return column.createTimeColumn() + of rdbTimestamp: + return column.createTimestampColumn() + of rdbTimestamps: + return column.createTimestampsColumn() + of rdbSoftDelete: + return column.createSoftDeleteColumn() + # others + of rdbBinary: + return column.createBlobColumn() + of rdbBoolean: + return column.createBoolColumn() + of rdbEnumField: + return column.createEnumColumn() + of rdbJson: + return column.createJsonColumn() + # foreign + of rdbForeign: + return column.createForeignColumn() + of rdbStrForeign: + return column.createStrForeignColumn() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/is_exists.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/is_exists.nim new file mode 100644 index 0000000..541c91e --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/mysql/sub/is_exists.nim @@ -0,0 +1,34 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import ../../../models/table +import ../../../models/column +import ../../../../query_builder/models/mysql/mysql_types +import ../../../../query_builder/models/mysql/mysql_query +import ../../../../query_builder/models/mysql/mysql_exec + + +proc isExistsIndex*(rdb:MysqlConnections, table:Table, column:Column):Future[bool] {.async.} = + let res = rdb.raw(&""" + SELECT count(*) as count + FROM `INFORMATION_SCHEMA`.`STATISTICS` + WHERE `TABLE_SCHEMA` = '{rdb.info.database}' + AND `TABLE_NAME` = '{table.name}' + AND `INDEX_NAME` = '{table.name}_{column.name}_index' + """) + .first() + .waitFor() + return res.isSome and res.get()["count"].getInt() > 0 + +proc isExistsUnique*(rdb:MysqlConnections, table:Table, column:Column):Future[bool] {.async.} = + let res = rdb.raw(&""" + SELECT count(*) as count + FROM `INFORMATION_SCHEMA`.`STATISTICS` + WHERE `TABLE_SCHEMA` = '{rdb.info.database}' + AND `TABLE_NAME` = '{table.name}' + AND `INDEX_NAME` = '{column.name}' + """) + .first() + .waitFor() + return res.isSome and res.get()["count"].getInt() > 0 diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/add_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/add_column.nim new file mode 100644 index 0000000..ff32db2 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/add_column.nim @@ -0,0 +1,21 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./postgres_query_type +import ./sub/add_column_query + + +proc addColumn*(self:PostgresSchema, isReset:bool) = + let queries = addColumnString(self.table, self.column) + + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/change_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/change_column.nim new file mode 100644 index 0000000..96dd242 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/change_column.nim @@ -0,0 +1,21 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./postgres_query_type +import ./sub/change_column_query + + +proc changeColumn*(self:PostgresSchema, isReset:bool) = + let queries = changeColumnString(self.table, self.column) + + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_migration_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_migration_table.nim new file mode 100644 index 0000000..6c3ea29 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_migration_table.nim @@ -0,0 +1,39 @@ +import std/strformat +import ../../enums +import ../../models/table +import ../../models/column +import ./postgres_query_type +import ./sub/create_column_query +import ./schema_utils + + +proc createMigrationTable*(self: PostgresSchema) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(self.table, column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignString(self.table, column)) + + if column.isIndex: + indexQuery.add(createIndexString(self.table, column)) + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query})" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + exec(self.rdb, queries) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_table.nim new file mode 100644 index 0000000..63fd889 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/create_table.nim @@ -0,0 +1,78 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/strutils +import std/sequtils +import std/json +import ../../enums +import ../../models/table +import ../../models/column +import ./postgres_query_type +import ./sub/create_column_query +import ./schema_utils + + +proc createTable*(self: PostgresSchema, isReset:bool) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + var updatedAtQuery:seq[string] = @[] + var commentQuery:seq[string] = @[] + + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(self.table, column)) + + if column.commentContent.len > 0: + commentQuery.add(createCommentColumn(self.table, column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignString(self.table, column)) + + if column.isIndex: + indexQuery.add(createIndexString(self.table, column)) + + if column.isUpdatedAt: + updatedAtQuery.add(createUpdatedAtString(self.table, column)) + + if self.table.primary.len > 0: + let primary = self.table.primary.map( + proc(row:string):string = + return &"\"{row}\"" + ) + .join(", ") + query.add(&", PRIMARY KEY({primary})") + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query})" + ) + + if self.table.commentContent.len > 0: + commentQuery.add( + &"COMMENT ON TABLE \"{self.table.name}\" IS '{self.table.commentContent}'" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + if updatedAtQuery.len > 0: + queries.add(updatedAtQuery) + + if commentQuery.len > 0: + queries.add(commentQuery) + + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_column.nim new file mode 100644 index 0000000..25f0a07 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_column.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import std/strformat +import ../../models/table +import ../../models/column +import ./schema_utils +import ./postgres_query_type + + +proc dropColumn*(self:PostgresSchema, isReset:bool) = + let query = &"ALTER TABLE \"{self.table.name}\" DROP COLUMN \"{self.column.name}\"" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_table.nim new file mode 100644 index 0000000..0a601ef --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/drop_table.nim @@ -0,0 +1,18 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./schema_utils +import ./postgres_query_type + + +proc dropTable*(self:PostgresSchema, isReset:bool) = + let query = &"DROP TABLE IF EXISTS \"{self.table.name}\"" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/postgres_query_type.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/postgres_query_type.nim new file mode 100644 index 0000000..657b59c --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/postgres_query_type.nim @@ -0,0 +1,15 @@ +import ../../../query_builder/models/postgres/postgres_types +import ../../models/table +import ../../models/column + + +type PostgresSchema* = ref object + rdb*:PostgresConnections + table*:Table + column*:Column + +proc new*(_:type PostgresSchema, rdb:PostgresConnections, table:Table):PostgresSchema = + return PostgresSchema(rdb:rdb, table:table) + +proc new*(_:type PostgresSchema, rdb:PostgresConnections, table:Table, column:Column):PostgresSchema = + return PostgresSchema(rdb:rdb, table:table, column:column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_column.nim new file mode 100644 index 0000000..c286449 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_column.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./postgres_query_type + + +proc renameColumn*(self:PostgresSchema, isReset:bool) = + let query = &"ALTER TABLE \"{self.table.name}\" RENAME COLUMN \"{self.column.previousName}\" TO \"{self.column.name}\"" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_table.nim new file mode 100644 index 0000000..253e3b9 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/rename_table.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./postgres_query_type +import ./schema_utils + + +proc renameTable*(self:PostgresSchema, isReset:bool) = + let query = &"ALTER TABLE \"{self.table.previousName}\" RENAME TO \"{self.table.name}\"" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/reset_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/reset_table.nim new file mode 100644 index 0000000..79e37ce --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/reset_table.nim @@ -0,0 +1,13 @@ +import std/asyncdispatch +import std/strformat +import ../../../query_builder/models/postgres/postgres_query +import ../../../query_builder/models/postgres/postgres_exec +import ../../models/table +import ./postgres_query_type + + +proc resetMigrationTable*(self:PostgresSchema) = + self.rdb.table("_allographer_migrations").where("name", "=", self.table.name).delete().waitFor + +proc resetTable*(self:PostgresSchema) = + self.rdb.raw(&"DROP TABLE IF EXISTS \"{self.table.name}\"").exec().waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/schema_utils.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/schema_utils.nim new file mode 100644 index 0000000..de75148 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/schema_utils.nim @@ -0,0 +1,109 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import std/strutils +import std/times +import ../../../query_builder/models/postgres/postgres_types +import ../../../query_builder/models/postgres/postgres_query +import ../../../query_builder/models/postgres/postgres_exec +import ../../../query_builder/error +import ../../models/table + + +proc notAllowedOption*(option, typ, column:string) = + ## {option} is not allowed in {typ} column {column} + raise newException(DbError, &"{option} is not allowed in {typ} column {column}") + +proc notAllowedType*(typ:string) = + ## Change to {typ} type is not allowed + raise newException(DbError, &"type {typ} is not allowed") + + +# ================================================== +# Postgres +# ================================================== + +proc shouldRun*(rdb:PostgresConnections, table:Table, checksum:string, isReset:bool):bool = + if isReset: + return true + + let history = rdb.table("_allographer_migrations") + .where("checksum", "=", checksum) + .first() + .waitFor + return not history.isSome() or not history.get()["status"].getBool + + +proc exec*(rdb:PostgresConnections, queries:seq[string]) = + var isSuccess = false + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + + try: + for query in queries: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + +proc execThenSaveHistory*(rdb:PostgresConnections, tableName:string, queries:seq[string], checksum:string) = + var isSuccess = false + try: + for query in queries: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let tableQuery = queries.join("; ") + let createdAt = (proc():string = + if rdb is PostgresConnections: + return $now().utc + # if rdb.type == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": tableQuery, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor + + +proc execThenSaveHistory*(rdb:PostgresConnections, tableName:string, query:string, checksum:string) = + var isSuccess = false + try: + rdb.raw(query).exec.waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let createdAt = (proc():string = + if rdb is PostgresConnections: + return $now().utc + # if rdb.driver == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": query, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/add_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/add_column_query.nim new file mode 100644 index 0000000..3774041 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/add_column_query.nim @@ -0,0 +1,508 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +# ============================================================================= +# int +# ============================================================================= +proc createSerialColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" SERIAL NOT NULL PRIMARY KEY" + return @[query] + + +proc createIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" INTEGER" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + query.add(&" CHECK (\"{column.name}\" >= 0)") + + return @[query] + + +proc createSmallIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" SMALLINT" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + query.add(&" CHECK (\"{column.name}\" >= 0)") + + return @[query] + + +proc createMediumIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" INTEGER" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + query.add(&" CHECK (\"{column.name}\" >= 0)") + + return @[query] + + +proc createBigIntColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" BIGINT" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + query.add(&" CHECK (\"{column.name}\" >= 0)") + + return @[query] + + +# ============================================================================= +# float +# ============================================================================= +proc createDecimalColumn(column:Column, table:Table):seq[string] = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" NUMERIC({maximum}, {digit})" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + query.add(&" CHECK (\"{column.name}\" >= 0)") + + return @[query] + + +proc createFloatColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" NUMERIC" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + query.add(&" CHECK (\"{column.name}\" >= 0)") + + return @[query] + + +# ============================================================================= +# char +# ============================================================================= +proc createCharColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" CHAR({maxLength})" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + return @[query] + + +proc createStringColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" VARCHAR({maxLength})" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + return @[query] + + +proc createTextColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" TEXT" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + return @[query] + + +# ============================================================================= +# date +# ============================================================================= +proc createDateColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" DATE" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + return @[query] + + +proc createDatetimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" TIMESTAMP" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + return @[query] + + +proc createTimeColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" TIME" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + return @[query] + + +proc createTimestampColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" TIMESTAMP" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + return @[query] + + +proc createTimestampsColumn(column:Column, table:Table):seq[string] = + var queries:seq[string] = @[] + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"created_at\" TIMESTAMP" + queries.add(query) + query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"updated_at\" TIMESTAMP DEFAULT (NOW())" + queries.add(query) + return queries + + +proc createSoftDeleteColumn(column:Column, table:Table):seq[string] = + let query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"deleted_at\" TIMESTAMP" + return @[query] + + +# ============================================================================= +# others +# ============================================================================= +proc createBlobColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" BYTEA" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + return @[query] + + +proc createBoolColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" BOOLEAN" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultBool}") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + return @[query] + + +proc enumOptionsGenerator(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(" OR ") + optionsString.add( + &"\"{name}\" = '{option}'" + ) + + return optionsString + + +proc createEnumColumn(column:Column, table:Table):seq[string] = + var queries:seq[string] + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" CHARACTER" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = enumOptionsGenerator(column.name, options) + query.add(&" CHECK ({optionsString})") + queries.add(query) + + return queries + + +proc createJsonColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" JSONB" + + if column.isUnique: + query.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultJson.pretty}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + return @[query] + + +proc addForeignColumn(column:Column, table:Table):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" INT" + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + return @[query] + + +proc addStrForeignColumn(column:Column, table:Table):seq[string] = + let maxLength = column.info["maxLength"].getInt + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN \"{column.name}\" VARCHAR({maxLength})" + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + return @[query] + + +proc addForeignKey(column:Column, table:Table):string = + var onDeleteString = "RESTRICT" + if column.foreignOnDelete == CASCADE: + onDeleteString = "CASCADE" + elif column.foreignOnDelete == SET_NULL: + onDeleteString = "SET NULL" + elif column.foreignOnDelete == NO_ACTION: + onDeleteString = "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"ALTER TABLE \"{table.name}\" ADD FOREIGN KEY (\"{column.name}\") REFERENCES \"{refTable}\"(\"{refColumn}\") ON DELETE {onDeleteString}" + + +proc addUpdatedAtColumn(column:Column, table:Table):seq[string] = + return @[ + &"""CREATE OR REPLACE FUNCTION {table.name}_updated_at_column() + RETURNS TRIGGER AS $$ + BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; + END; + $$ LANGUAGE plpgsql;""", + + &"""CREATE TRIGGER set_{table.name}_updated_at + BEFORE UPDATE ON "{table.name}" + FOR EACH ROW + EXECUTE FUNCTION {table.name}_updated_at_column();""" + ] + + +proc addIndexColumn(column:Column, table:Table):string = + return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"(\"{column.name}\")" + + +proc addCommentColumn(column:Column, table:Table):string = + return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS '{column.commentContent}'" + + +proc addNullCommentColumn(column:Column, table:Table):string = + return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS NULL" + + +proc addColumnString*(table:Table, column:Column):seq[string] = + var queries:seq[string] + case column.typ: + # int + of rdbIncrements: + queries = column.createSerialColumn(table) + of rdbInteger: + queries = column.createIntColumn(table) + of rdbSmallInteger: + queries = column.createSmallIntColumn(table) + of rdbMediumInteger: + queries = column.createMediumIntColumn(table) + of rdbBigInteger: + queries = column.createBigIntColumn(table) + # float + of rdbDecimal: + queries = column.createDecimalColumn(table) + of rdbDouble: + queries = column.createDecimalColumn(table) + of rdbFloat: + queries = column.createFloatColumn(table) + # char + of rdbUuid: + queries = column.createStringColumn(table) + of rdbChar: + queries = column.createCharColumn(table) + of rdbString: + queries = column.createStringColumn(table) + # text + of rdbText: + queries = column.createTextColumn(table) + of rdbMediumText: + queries = column.createTextColumn(table) + of rdbLongText: + queries = column.createTextColumn(table) + # date + of rdbDate: + queries = column.createDateColumn(table) + of rdbDatetime: + queries = column.createDatetimeColumn(table) + of rdbTime: + queries = column.createTimeColumn(table) + of rdbTimestamp: + queries = column.createTimestampColumn(table) + of rdbTimestamps: + queries = column.createTimestampsColumn(table) + of rdbSoftDelete: + queries = column.createSoftDeleteColumn(table) + # others + of rdbBinary: + queries = column.createBlobColumn(table) + of rdbBoolean: + queries = column.createBoolColumn(table) + of rdbEnumField: + queries = column.createEnumColumn(table) + of rdbJson: + queries = column.createJsonColumn(table) + # foreign + of rdbForeign: + queries = column.addForeignColumn(table) + queries.add(column.addForeignKey(table)) + of rdbStrForeign: + queries = column.addStrForeignColumn(table) + queries.add(column.addForeignKey(table)) + + if column.isIndex: + queries.add(column.addIndexColumn(table)) + + if column.isUpdatedAt: + queries.add(column.addUpdatedAtColumn(table)) + + if column.commentContent.len > 0: + queries.add(column.addCommentColumn(table)) + elif column.isCommentNull: + queries.add(column.addNullCommentColumn(table)) + + return queries diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/change_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/change_column_query.nim new file mode 100644 index 0000000..589b587 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/change_column_query.nim @@ -0,0 +1,429 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +proc commonSetup(column:Column, table:Table):seq[string] = + ## drop default, drop unique, drop enum, drop fkey, drop index + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" DROP DEFAULT") + result.add(&"ALTER TABLE \"{table.name}\" DROP CONSTRAINT IF EXISTS \"{table.name}_{column.name}_unique\"") + result.add(&"ALTER TABLE \"{table.name}\" DROP CONSTRAINT IF EXISTS \"{table.name}_{column.name}_enum\"") + result.add(&"ALTER TABLE \"{table.name}\" DROP CONSTRAINT IF EXISTS \"{table.name}_{column.name}_fkey\"") + result.add(&"DROP INDEX IF EXISTS \"{table.name}_{column.name}_index\"") + + +# ============================================================================= +# int +# ============================================================================= +proc changeIntColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE INTEGER") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT {column.defaultInt}") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{column.name}\" CHECK (\"{column.name}\" >= 0)") + + +proc changeSmallIntColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE SMALLINT") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT {column.defaultInt}") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{column.name}\" CHECK (\"{column.name}\" >= 0)") + + +proc changeMediumIntColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE INTEGER") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT {column.defaultInt}") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{column.name}\" CHECK (\"{column.name}\" >= 0)") + + +proc changeBigIntColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE BIGINT") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT {column.defaultInt}") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{column.name}\" CHECK (\"{column.name}\" >= 0)") + + +# ============================================================================= +# float +# ============================================================================= +proc changeDecimalColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE NUMERIC({maximum}, {digit})") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT {column.defaultFloat}") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{column.name}\" CHECK (\"{column.name}\" >= 0)") + + +proc changeFloatColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE NUMERIC") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT {column.defaultFloat}") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{column.name}\" CHECK (\"{column.name}\" >= 0)") + + +# ============================================================================= +# char +# ============================================================================= +proc changeCharColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + + let maxLength = column.info["maxLength"].getInt + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE CHAR({maxLength})") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT '{column.defaultString}'") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + +proc changeStringColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + + let maxLength = column.info["maxLength"].getInt + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE VARCHAR({maxLength})") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT '{column.defaultString}'") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + +proc changeTextColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE TEXT") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT '{column.defaultString}'") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + +# ============================================================================= +# date +# ============================================================================= +proc changeDateColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE DATE") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT (NOW())") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + +proc changeDatetimeColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE TIMESTAMP") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT (NOW())") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + +proc changeTimeColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE TIME") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT (NOW())") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + +proc changeTimestampColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE TIMESTAMP") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT (NOW())") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + +# ============================================================================= +# others +# ============================================================================= +proc changeBlobColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE BYTEA") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT '{column.defaultString}'") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + +proc changeBoolColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE BOOLEAN") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT {column.defaultBool}") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + +proc enumOptionsGenerator(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(" OR ") + optionsString.add( + &"\"{name}\" = '{option}'" + ) + + return optionsString + + +proc changeEnumColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE CHARACTER") + + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + let optionsString = enumOptionsGenerator(column.name, options) + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_enum\" CHECK ({optionsString})") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT '{column.defaultString}'") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + +proc changeJsonColumn(column:Column, table:Table):seq[string] = + result = commonSetup(column, table) + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" TYPE JSONB") + + if column.isDefault: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET DEFAULT '{column.defaultJson.pretty}'") + + if column.isUnique: + result.add(&"ALTER TABLE \"{table.name}\" ADD CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE (\"{column.name}\")") + + if not column.isNullable: + result.add(&"ALTER TABLE \"{table.name}\" ALTER COLUMN \"{column.name}\" SET NOT NULL") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + +proc indexColumn(column:Column, table:Table):string = + return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"(\"{column.name}\")" + + +proc addCommentColumn(column:Column, table:Table):string = + return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS '{column.commentContent}'" + + +proc addNullCommentColumn(column:Column, table:Table):string = + return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS NULL" + + +proc changeColumnString*(table:Table, column:Column):seq[string] = + var queries:seq[string] + case column.typ: + # int + of rdbIncrements: + queries = column.changeIntColumn(table) + of rdbInteger: + queries = column.changeIntColumn(table) + of rdbSmallInteger: + queries = column.changeSmallIntColumn(table) + of rdbMediumInteger: + queries = column.changeMediumIntColumn(table) + of rdbBigInteger: + queries = column.changeBigIntColumn(table) + # float + of rdbDecimal: + queries = column.changeDecimalColumn(table) + of rdbDouble: + queries = column.changeDecimalColumn(table) + of rdbFloat: + queries = column.changeFloatColumn(table) + # char + of rdbUuid: + queries = column.changeStringColumn(table) + of rdbChar: + queries = column.changeCharColumn(table) + of rdbString: + queries = column.changeStringColumn(table) + # text + of rdbText: + queries = column.changeTextColumn(table) + of rdbMediumText: + queries = column.changeTextColumn(table) + of rdbLongText: + queries = column.changeTextColumn(table) + # date + of rdbDate: + queries = column.changeDateColumn(table) + of rdbDatetime: + queries = column.changeDatetimeColumn(table) + of rdbTime: + queries = column.changeTimeColumn(table) + of rdbTimestamp: + queries = column.changeTimestampColumn(table) + of rdbTimestamps: + notAllowedType("timestamps") + of rdbSoftDelete: + notAllowedType("softDelete") + # others + of rdbBinary: + queries = column.changeBlobColumn(table) + of rdbBoolean: + queries = column.changeBoolColumn(table) + of rdbEnumField: + queries = column.changeEnumColumn(table) + of rdbJson: + queries = column.changeJsonColumn(table) + # foreign + of rdbForeign: + notAllowedType("foreign") + of rdbStrForeign: + notAllowedType("strForeign") + + if column.isIndex and column.typ != rdbIncrements: + queries.add(indexColumn(column, table)) + + if column.commentContent.len > 0: + queries.add(column.addCommentColumn(table)) + elif column.isCommentNull: + queries.add(column.addNullCommentColumn(table)) + + return queries diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/create_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/create_column_query.nim new file mode 100644 index 0000000..6b0f8fa --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/postgres/sub/create_column_query.nim @@ -0,0 +1,466 @@ +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +# ============================================================================= +# int +# ============================================================================= +proc createSerialColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" BIGSERIAL NOT NULL PRIMARY KEY" + + +proc createIntColumn(column:Column, table:Table):string = + if column.isAutoIncrement: + result = &"\"{column.name}\" BIGSERIAL" + else: + result = &"\"{column.name}\" INTEGER" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + result.add(&" CHECK (\"{column.name}\" >= 0)") + + +proc createSmallIntColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" SMALLINT" + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "smallint", column.name) + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + result.add(&" CHECK (\"{column.name}\" >= 0)") + + +proc createMediumIntColumn(column:Column, table:Table):string = + if column.isAutoIncrement: + result = &"\"{column.name}\" BIGSERIAL" + else: + result = &"\"{column.name}\" INTEGER" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + result.add(&" CHECK (\"{column.name}\" >= 0)") + + +proc createBigIntColumn(column:Column, table:Table):string = + if column.isAutoIncrement: + result = &"\"{column.name}\" BIGSERIAL" + else: + result = &"\"{column.name}\" BIGINT" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + result.add(&" CHECK (\"{column.name}\" >= 0)") + + +# ============================================================================= +# float +# ============================================================================= +proc createDecimalColumn(column:Column, table:Table):string = + let maximum = column.info["maximum"].getInt + let digit = column.info["digit"].getInt + result = &"\"{column.name}\" NUMERIC({maximum}, {digit})" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + result.add(&" CHECK (\"{column.name}\" >= 0)") + + +proc createFloatColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" NUMERIC" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + result.add(&" CHECK (\"{column.name}\" >= 0)") + + +# ============================================================================= +# char +# ============================================================================= +proc createCharColumn(column:Column, table:Table):string = + let maxLength = column.info["maxLength"].getInt + result = &"\"{column.name}\" CHAR({maxLength})" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + +proc createStringColumn(column:Column, table:Table):string = + let maxLength = column.info["maxLength"].getInt + result = &"\"{column.name}\" VARCHAR({maxLength})" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "string", column.name) + + +proc createTextColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" TEXT" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + +# ============================================================================= +# date +# ============================================================================= +proc createDateColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" DATE" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + +proc createDatetimeColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" TIMESTAMP" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + +proc createTimeColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" TIME" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + +proc createTimestampColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" TIMESTAMP" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT (NOW())") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + +proc createTimestampsColumn(column:Column, table:Table):string = + result = "\"created_at\" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " + result.add("\"updated_at\" TIMESTAMP DEFAULT CURRENT_TIMESTAMP") + + +proc createSoftDeleteColumn(column:Column, table:Table):string = + result = "\"deleted_at\" TIMESTAMP" + + +# ============================================================================= +# others +# ============================================================================= +proc createBlobColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" BYTEA" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + +proc createBoolColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" BOOLEAN" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultBool}") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + +proc enumOptionsGenerator(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(" OR ") + optionsString.add( + &"\"{name}\" = '{option}'" + ) + + return optionsString + + +proc createEnumColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" CHARACTER" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = enumOptionsGenerator(column.name, options) + result.add(&" CHECK ({optionsString})") + + +proc createJsonColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" JSONB" + + if column.isUnique: + result.add(&" CONSTRAINT \"{table.name}_{column.name}_unique\" UNIQUE") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultJson.pretty}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + +proc createForeignColumn(column:Column, table:Table):string = + result = &"\"{column.name}\" INT" + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + +proc createStrForeignColumn(column:Column, table:Table):string = + let maxLength = column.info["maxLength"].getInt + result = &"\"{column.name}\" VARCHAR({maxLength})" + + if column.isDefault: + result.add(&" DEFAULT {column.defaultString}") + + +proc createForeignKey(column:Column, table:Table):string = + var onDeleteString = "RESTRICT" + if column.foreignOnDelete == CASCADE: + onDeleteString = "CASCADE" + elif column.foreignOnDelete == SET_NULL: + onDeleteString = "SET NULL" + elif column.foreignOnDelete == NO_ACTION: + onDeleteString = "NO ACTION" + + let refColumn = column.info["column"].getStr + let refTable = column.info["table"].getStr + return &"FOREIGN KEY(\"{column.name}\") REFERENCES \"{refTable}\"(\"{refColumn}\") ON DELETE {onDeleteString}" + + +proc indexColumn(column:Column, table:Table):string = + return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"(\"{column.name}\")" + + +proc updatedAtColumn(column:Column, table:Table):seq[string] = + return @[ + &"""CREATE OR REPLACE FUNCTION {table.name}_updated_at_column() + RETURNS TRIGGER AS $$ + BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; + END; + $$ LANGUAGE plpgsql;""", + + &"""CREATE TRIGGER set_{table.name}_updated_at + BEFORE UPDATE ON "{table.name}" + FOR EACH ROW + EXECUTE FUNCTION {table.name}_updated_at_column();""" + ] + + +proc createColumnString*(table:Table, column:Column):string = + case column.typ: + # int + of rdbIncrements: + return column.createSerialColumn(table) + of rdbInteger: + return column.createIntColumn(table) + of rdbSmallInteger: + return column.createSmallIntColumn(table) + of rdbMediumInteger: + return column.createMediumIntColumn(table) + of rdbBigInteger: + return column.createBigIntColumn(table) + # float + of rdbDecimal: + return column.createDecimalColumn(table) + of rdbDouble: + return column.createDecimalColumn(table) + of rdbFloat: + return column.createFloatColumn(table) + # char + of rdbUuid: + return column.createStringColumn(table) + of rdbChar: + return column.createCharColumn(table) + of rdbString: + return column.createStringColumn(table) + # text + of rdbText: + return column.createTextColumn(table) + of rdbMediumText: + return column.createTextColumn(table) + of rdbLongText: + return column.createTextColumn(table) + # date + of rdbDate: + return column.createDateColumn(table) + of rdbDatetime: + return column.createDatetimeColumn(table) + of rdbTime: + return column.createTimeColumn(table) + of rdbTimestamp: + return column.createTimestampColumn(table) + of rdbTimestamps: + return column.createTimestampsColumn(table) + of rdbSoftDelete: + return column.createSoftDeleteColumn(table) + # others + of rdbBinary: + return column.createBlobColumn(table) + of rdbBoolean: + return column.createBoolColumn(table) + of rdbEnumField: + return column.createEnumColumn(table) + of rdbJson: + return column.createJsonColumn(table) + # foreign + of rdbForeign: + return column.createForeignColumn(table) + of rdbStrForeign: + return column.createStrForeignColumn(table) + + +proc createForeignString*(table:Table, column:Column):string = + return column.createForeignKey(table) + + +proc createIndexString*(table:Table, column:Column):string = + return column.indexColumn(table) + + +proc createUpdatedAtString*(table:Table, column:Column):seq[string] = + return column.updatedAtColumn(table) + + +proc createCommentColumn*(table:Table, column:Column):string = + return &"COMMENT ON COLUMN \"{table.name}\".\"{column.name}\" IS '{column.commentContent}'" diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/add_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/add_column.nim new file mode 100644 index 0000000..cbc53fb --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/add_column.nim @@ -0,0 +1,50 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import ../../models/table +import ../../models/column +import ./sqlite_query_type +import ./sub/add_column_query +import ./schema_utils + + +# proc shouldRun(rdb:SqliteConnections, table:Table, column:Column, checksum:string, isReset:bool):bool = +# if isReset: +# return true + +# let history = rdb.table("_allographer_migrations") +# .where("checksum", "=", checksum) +# .first() +# .waitFor +# return not history.isSome() or not history.get()["status"].getBool + + +# proc execThenSaveHistory(rdb:SqliteConnections, tableName:string, queries:seq[string], checksum:string) = +# var isSuccess = false +# try: +# for query in queries: +# rdb.raw(query).exec.waitFor +# isSuccess = true +# except DbError: +# echo getCurrentExceptionMsg() + +# let tableQuery = queries.join("; ") +# rdb.table("_allographer_migrations").insert(%*{ +# "name": tableName, +# "query": tableQuery, +# "checksum": checksum, +# "created_at": $now().utc, +# "status": isSuccess +# }) +# .waitFor + +proc addColumn*(self:SqliteSchema, isReset:bool) = + let queries = addColumnString(self.rdb, self.table, self.column) + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/change_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/change_column.nim new file mode 100644 index 0000000..75fb32b --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/change_column.nim @@ -0,0 +1,86 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/asyncdispatch +import std/strformat +import std/re +import std/json +import ../../../query_builder/models/sqlite/sqlite_query +import ../../../query_builder/models/sqlite/sqlite_exec +import ../../models/table +import ../../models/column +import ../../enums +import ./schema_utils +import ./sqlite_query_type +import ./sub/create_column_query + + +proc changeColumn*(self:SqliteSchema, isReset:bool) = + ## - create tmp table with new column difinition + ## - copy data from old table to tmp table + ## - delete old table + ## - rename tmp table name to old table name + + # TODO: How can I raise error in compire time? + case self.column.typ + of rdbIncrements: + notAllowedType("increments") + of rdbTimestamps: + notAllowedType("timestamps") + of rdbSoftDelete: + notAllowedType("softDelete") + of rdbForeign: + notAllowedType("foreign") + of rdbStrForeign: + notAllowedType("strForeign") + else: + discard + + let tableDifinitionSql = &"SELECT sql FROM sqlite_master WHERE type = 'table' AND tbl_name = '{self.table.name}'" + var rows = self.rdb.raw(tableDifinitionSql).get().waitFor + var schema = replace(rows[0]["sql"].getStr, re"\)$", ",)") + let columnQuery = createColumnString(self.column) + + # delete existing foreign key + var regex = &"\\sFOREIGN KEY\\('{self.column.name}'\\).+?\\," + schema = schema.replace(re(regex), "") + + # replace new column definition + var queries:seq[string] = @[] + regex = &"'{self.column.name}'\\s+.*?," + var query = schema.replace(re(regex), columnQuery & ",") + if self.column.typ == rdbForeign or self.column.typ == rdbStrForeign: + let foreignQuery = createForeignString(self.column) + query = query.replace(re",\s*\)", &", {foreignQuery},)") + query = query.replace(re",\s*\)$", ")") + query = query.replace(re("CREATE TABLE \"\\w+\""), &"CREATE TABLE \"alter_{self.table.name}\"") + queries.add(query) + + # copy data from existing table to tmp table + query = &"INSERT INTO \"alter_{self.table.name}\" SELECT * FROM \"{self.table.name}\"" + queries.add(query) + # delete existing table + query = &"DROP TABLE IF EXISTS \"{self.table.name}\"" + queries.add(query) + # rename tmp table to existing table + query = &"ALTER TABLE \"alter_{self.table.name}\" RENAME TO \"{self.table.name}\"" + queries.add(query) + + # create index for target column + if self.column.isIndex: + query = createIndexString(self.table, self.column) + queries.add(query) + # recreate index + let indexDinifitionSql = &"SELECT sql, name FROM sqlite_master WHERE type = 'index' AND tbl_name = '{self.table.name}'" + rows = self.rdb.raw(indexDinifitionSql).get().waitFor + for row in rows: + if row["name"].getStr != &"{self.table.name}_{self.column.name}_index" and row["sql"].kind != JNull: + queries.add(row["sql"].getStr) + + let jsonSchema = $self.column.toSchema() + let checksum = $jsonSchema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_migration_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_migration_table.nim new file mode 100644 index 0000000..1f07a62 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_migration_table.nim @@ -0,0 +1,38 @@ +import std/strformat +import ../../enums +import ../../models/table +import ../../models/column +import ./sqlite_query_type +import ./sub/create_column_query +import ./schema_utils + + +proc createMigrationTable*(self: SqliteSchema) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignString(column)) + + if column.isIndex: + indexQuery.add(createIndexString(self.table, column)) + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query})" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + exec(self.rdb, queries) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_table.nim new file mode 100644 index 0000000..abfc606 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/create_table.nim @@ -0,0 +1,59 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/strutils +import std/sequtils +import std/json +import ../../enums +import ../../models/table +import ../../models/column +import ./schema_utils +import ./sqlite_query_type +import ./sub/create_column_query + + +proc createTable*(self: SqliteSchema, isReset:bool) = + var queries:seq[string] = @[] + var query = "" + var foreignQuery = "" + var indexQuery:seq[string] = @[] + + for i, column in self.table.columns: + if query.len > 0: query.add(", ") + query.add(createColumnString(column)) + + if column.typ == rdbForeign or column.typ == rdbStrForeign: + if foreignQuery.len > 0: foreignQuery.add(", ") + foreignQuery.add(createForeignString(column)) + + if column.isIndex: + indexQuery.add(createIndexString(self.table, column)) + + if self.table.primary.len > 0: + let primary = self.table.primary.map( + proc(row:string):string = + return &"\"{row}\"" + ) + .join(", ") + query.add(&", PRIMARY KEY({primary})") + + if foreignQuery.len > 0: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query}, {foreignQuery})" + ) + else: + queries.add( + &"CREATE TABLE IF NOT EXISTS \"{self.table.name}\" ({query})" + ) + + if indexQuery.len > 0: + queries.add(indexQuery) + + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_column.nim new file mode 100644 index 0000000..17e5b0d --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_column.nim @@ -0,0 +1,57 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/asyncdispatch +import std/strutils +import std/strformat +import std/sequtils +import std/re +import std/json +import ../../../query_builder/models/sqlite/sqlite_query +import ../../../query_builder/models/sqlite/sqlite_exec +import ../../models/table +import ../../models/column +import ./schema_utils +import ./sqlite_query_type + + +proc dropColumn*(self:SqliteSchema, isReset:bool) = + ## - create tmp table with new column difinition + ## - copy data from old table to tmp table + ## - delete old table + ## - rename tmp table name to old table name + + let tableDifinitionSql = &"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = '{self.table.name}'" + var rows = self.rdb.raw(tableDifinitionSql).get().waitFor + let schema = replace(rows[0]["sql"].getStr, re"\)$", ",)") + + var queries:seq[string] = @[] + var columnRegex = &"'{self.column.name}'\\s+.*?," + var query = schema.replace(re(columnRegex), "") + query = query.replace(re",\s*\)$", ")") + query = query.replace(re("CREATE TABLE \"\\w+\""), &"CREATE TABLE \"alter_{self.table.name}\"") + queries.add(query) + + var columns = self.rdb.table(self.table.name).columns().waitFor + columns = columns.filter( + proc(x:string):bool = + return x != self.column.name + ) + for i, row in columns: + columns[i] = &"'{row}'" + let columnsString = columns.join(", ") + query = &"INSERT INTO \"alter_{self.table.name}\"({columnsString}) SELECT {columnsString} FROM \"{self.table.name}\"" + queries.add(query) + # delete existing table + query = &"DROP TABLE IF EXISTS \"{self.table.name}\"" + queries.add(query) + # rename tmp table to existing table + query = &"ALTER TABLE \"alter_{self.table.name}\" RENAME TO \"{self.table.name}\"" + queries.add(query) + let jsonSchema = $self.column.toSchema() + let checksum = $jsonSchema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_table.nim new file mode 100644 index 0000000..4ab5909 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/drop_table.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./sqlite_query_type +import ./schema_utils + + +proc dropTable*(self:SqliteSchema, isReset:bool) = + let query = &"DROP TABLE IF EXISTS \"{self.table.name}\"" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_column.nim new file mode 100644 index 0000000..8dbb169 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_column.nim @@ -0,0 +1,20 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./sqlite_query_type + + +proc renameColumn*(self:SqliteSchema, isReset:bool) = + let query = &"ALTER TABLE \"{self.table.name}\" RENAME COLUMN '{self.column.previousName}' TO '{self.column.name}'" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_table.nim new file mode 100644 index 0000000..b0a8afd --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/rename_table.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/json +import ../../models/table +import ./sqlite_query_type +import ./schema_utils + + +proc renameTable*(self:SqliteSchema, isReset:bool) = + let query = &"ALTER TABLE \"{self.table.previousName}\" RENAME TO \"{self.table.name}\"" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/reset_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/reset_table.nim new file mode 100644 index 0000000..9317381 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/reset_table.nim @@ -0,0 +1,13 @@ +import std/asyncdispatch +import std/strformat +import ../../../query_builder/models/sqlite/sqlite_query +import ../../../query_builder/models/sqlite/sqlite_exec +import ../../models/table +import ./sqlite_query_type + + +proc resetMigrationTable*(self:SqliteSchema) = + self.rdb.table("_allographer_migrations").where("name", "=", self.table.name).delete().waitFor + +proc resetTable*(self:SqliteSchema) = + self.rdb.raw(&"DROP TABLE IF EXISTS \"{self.table.name}\"").exec().waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/schema_utils.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/schema_utils.nim new file mode 100644 index 0000000..e25c4d7 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/schema_utils.nim @@ -0,0 +1,116 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import std/strutils +import std/times +import ../../../query_builder/models/sqlite/sqlite_types +import ../../../query_builder/models/sqlite/sqlite_query +import ../../../query_builder/models/sqlite/sqlite_exec +import ../../../query_builder/error +import ../../models/column +import ../../models/table + + +proc notAllowedOption*(option, typ, column:string) = + ## {option} is not allowed in {typ} column {column} + raise newException(DbError, &"{option} is not allowed in {typ} column {column}") + +proc notAllowedType*(typ:string) = + ## Change to {typ} type is not allowed + raise newException(DbError, &"type {typ} is not allowed") + + +proc sqliteLengthCheck*(column:Column, maxLength:int):string = + ## "CHECK (length({column.name}) <= {maxLength})" + let escapedColumnName = column.name.replace("\"", "\"\"") + return &" CHECK (length(\"{escapedColumnName}\") <= {maxLength})" + + +# ================================================== +# Sqlite +# ================================================== + +proc shouldRun*(rdb:SqliteConnections, table:Table, checksum:string, isReset:bool):bool = + if isReset: + return true + + let history = rdb.table("_allographer_migrations") + .where("checksum", "=", checksum) + .first() + .waitFor + return not history.isSome() or not history.get()["status"].getBool + + +proc exec*(rdb:SqliteConnections, queries:seq[string]) = + var isSuccess = false + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + + try: + for query in queries: + rdb.raw(query).exec().waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + +proc execThenSaveHistory*(rdb:SqliteConnections, tableName:string, queries:seq[string], checksum:string) = + var isSuccess = false + try: + for query in queries: + rdb.raw(query).exec().waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let tableQuery = queries.join("; ") + let createdAt = (proc():string = + if rdb is SqliteConnections: + return $now().utc + # if rdb.type == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": tableQuery, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor + + +proc execThenSaveHistory*(rdb:SqliteConnections, tableName:string, query:string, checksum:string) = + var isSuccess = false + try: + rdb.raw(query).exec().waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let createdAt = (proc():string = + if rdb is SqliteConnections: + return $now().utc + # if rdb.driver == SQLite3 or rdb.driver == PostgreSQL: + # return $now().utc + # elif rdb.driver == MariaDB or rdb.driver == MySQL: + # return now().utc.format("yyyy-MM-dd HH:mm:ss'.'fff") + )() + + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": query, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sqlite_query_type.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sqlite_query_type.nim new file mode 100644 index 0000000..e89406a --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sqlite_query_type.nim @@ -0,0 +1,14 @@ +import ../../../query_builder/models/sqlite/sqlite_types +import ../../models/table +import ../../models/column + +type SqliteSchema* = ref object + rdb*:SqliteConnections + table*:Table + column*:Column + +proc new*(_:type SqliteSchema, rdb:SqliteConnections, table:Table):SqliteSchema = + return SqliteSchema(rdb:rdb, table:table) + +proc new*(_:type SqliteSchema, rdb:SqliteConnections, table:Table, column:Column):SqliteSchema = + return SqliteSchema(rdb:rdb, table:table, column:column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/add_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/add_column_query.nim new file mode 100644 index 0000000..6bed187 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/add_column_query.nim @@ -0,0 +1,437 @@ +## https://www.sqlite.org/lang_createtable.html#constraints + +import std/asyncdispatch +import std/json +import std/strformat +import std/strutils +import std/re +import ../../../../query_builder/models/sqlite/sqlite_types +import ../../../../query_builder/models/sqlite/sqlite_query +import ../../../../query_builder/models/sqlite/sqlite_exec +import ../../../models/table +import ../../../models/column +import ../../../enums +import ../schema_utils +import ./create_column_query + +# ============================================================================= +# int +# ============================================================================= +proc addSerialColumn(rdb:SqliteConnections, table:Table, column:Column, query:string):seq[string] = + # get culumn definition + let tableDifinitionSql = &"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = '{table.name}'" + var rows = rdb.raw(tableDifinitionSql).get().waitFor + let schema = replace(rows[0]["sql"].getStr, re"\)$", ",)") + var query = schema.replace(re",\)", &", {query},)") + query = query.replace(re",\)", ")") + query = query.replace(re("CREATE TABLE \"\\w+\""), &"CREATE TABLE \"alter_{table.name}\"") + result.add(query) + # copy data from existing table to tmp table + var columns = rdb.table(table.name).columns().waitFor + for i, row in columns: + columns[i] = &"'{row}'" + let columnsString = columns.join(", ") + query = &"INSERT INTO \"alter_{table.name}\"({columnsString}) SELECT {columnsString} FROM \"{table.name}\"" + result.add(query) + # delete existing table + query = &"DROP TABLE IF EXISTS \"{table.name}\"" + result.add(query) + # rename tmp table to existing table + query = &"ALTER TABLE \"alter_{table.name}\" RENAME TO \"{table.name}\"" + result.add(query) + + +proc addIntColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' INTEGER" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + query.add(&" CHECK ({column.name} >= 0)") + + return @[query] + + +# ============================================================================= +# float +# ============================================================================= +proc addDecimalColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' NUMERIC" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + query.add(&" CHECK ({column.name} >= 0)") + + return @[query] + + +proc addFloatColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' REAL" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + query.add(&" CHECK ({column.name} >= 0)") + + return @[query] + + +# ============================================================================= +# char +# ============================================================================= +proc addUuidColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' VARCHAR" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + let maxLength = column.info["maxLength"].getInt + query.add(sqliteLengthCheck(column, maxLength)) + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + return @[query] + + +proc addCharColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' VARCHAR" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + let maxLength = column.info["maxLength"].getInt + query.add(sqliteLengthCheck(column, maxLength)) + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + return @[query] + + +proc addVarcharColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' VARCHAR" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + let maxLength = column.info["maxLength"].getInt + query.add(sqliteLengthCheck(column, maxLength)) + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + return @[query] + + +proc addTextColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' TEXT" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + return @[query] + + +# ============================================================================= +# date +# ============================================================================= +proc addDateColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' DATE" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + return @[query] + + +proc addDatetimeColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' DATETIME" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + return @[query] + + +proc addTimeColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' TIME" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + return @[query] + + +proc addTimestampColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' DATETIME" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + return @[query] + + +proc addTimestampsColumn(table:Table, column:Column):seq[string] = + result.add(&"ALTER TABLE \"{table.name}\" ADD COLUMN 'created_at' DATETIME DEFAULT CURRENT_TIMESTAMP") + result.add(&"ALTER TABLE \"{table.name}\" ADD COLUMN 'updated_at' DATETIME DEFAULT CURRENT_TIMESTAMP") + + +proc addSoftDeleteColumn(table:Table, column:Column):seq[string] = + return @[&"ALTER TABLE \"{table.name}\" ADD COLUMN 'deleted_at' DATETIME"] + + +# ============================================================================= +# others +# ============================================================================= +proc addBlobColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' BLOB" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + return @[query] + + +proc addBoolColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' TINYINT" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT {column.defaultBool}") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + return @[query] + + +proc enumOptions(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(" OR ") + optionsString.add( + &"{name} = '{option}'" + ) + + return optionsString + + +proc addEnumColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' VARCHAR" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultString}'") + + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = enumOptions(column.name, options) + query.add(&" CHECK ({optionsString})") + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + return @[query] + + +proc addJsonColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' JSON" + + if not column.isNullable: + query.add(" NOT NULL") + + if column.isDefault: + query.add(&" DEFAULT '{column.defaultJson.pretty}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + return @[query] + + +# ============================================================================= +# foreign key +# ============================================================================= +proc addForeignKey(column:Column):string = + var onDeleteString = "RESTRICT" + if column.foreignOnDelete == CASCADE: + onDeleteString = "CASCADE" + elif column.foreignOnDelete == SET_NULL: + onDeleteString = "SET NULL" + elif column.foreignOnDelete == NO_ACTION: + onDeleteString = "NO ACTION" + + let tableName = column.info["table"].getStr + let columnName = column.info["column"].getStr + + return &" REFERENCES \"{tableName}\"('{columnName}') ON DELETE {onDeleteString}" + + +proc addForeignColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' INTEGER" + if column.isDefault: + query.add(&" DEFAULT {column.defaultInt}") + + query.add(addForeignKey(column)) + + return @[query] + + +proc addStrForeignColumn(table:Table, column:Column):seq[string] = + var query = &"ALTER TABLE \"{table.name}\" ADD COLUMN '{column.name}' VARCHAR" + if column.isDefault: + query.add(&" DEFAULT {column.defaultString}") + + query.add(addForeignKey(column)) + + return @[query] + + +proc addUniqueColumn(column:Column, table:Table):string = + return &"CREATE UNIQUE INDEX IF NOT EXISTS \"{table.name}_{column.name}_unique\" ON \"{table.name}\"('{column.name}')" + + +proc addIndexColumn(column:Column, table:Table):string = + return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"('{column.name}')" + + +proc addColumnString*(rdb:SqliteConnections, table:Table, column:Column):seq[string] = + var queries:seq[string] + case column.typ + of rdbIncrements: + let query = createColumnString(column) + queries.add(addSerialColumn(rdb, table, column, query)) + of rdbInteger: + queries.add(addIntColumn(table, column)) + of rdbSmallInteger: + queries.add(addIntColumn(table, column)) + of rdbMediumInteger: + queries.add(addIntColumn(table, column)) + of rdbBigInteger: + queries.add(addIntColumn(table, column)) + # float + of rdbDecimal: + queries.add(addDecimalColumn(table, column)) + of rdbDouble: + queries.add(addDecimalColumn(table, column)) + of rdbFloat: + queries.add(addFloatColumn(table, column)) + # char + of rdbUuid: + queries.add(addUuidColumn(table, column)) + of rdbChar: + queries.add(addCharColumn(table, column)) + of rdbString: + queries.add(addVarcharColumn(table, column)) + # text + of rdbText: + queries.add(addTextColumn(table, column)) + of rdbMediumText: + queries.add(addTextColumn(table, column)) + of rdbLongText: + queries.add(addTextColumn(table, column)) + # date + of rdbDate: + queries.add(addDateColumn(table, column)) + of rdbDatetime: + queries.add(addDatetimeColumn(table, column)) + of rdbTime: + queries.add(addTimeColumn(table, column)) + of rdbTimestamp: + queries.add(addTimestampColumn(table, column)) + of rdbTimestamps: + queries.add(addTimestampsColumn(table, column)) + of rdbSoftDelete: + queries.add(addSoftDeleteColumn(table, column)) + # others + of rdbBinary: + queries.add(addBlobColumn(table, column)) + of rdbBoolean: + queries.add(addBoolColumn(table, column)) + of rdbEnumField: + queries.add(addEnumColumn(table, column)) + of rdbJson: + queries.add(addJsonColumn(table, column)) + # foreign + of rdbForeign: + queries.add(addForeignColumn(table, column)) + of rdbStrForeign: + queries.add(addStrForeignColumn(table, column)) + + let indexQuery = + if column.isUnique: + column.addUniqueColumn(table) + elif column.isIndex: + column.addIndexColumn(table) + else: + "" + + if indexQuery.len > 0: + queries.add(indexQuery) + + return queries diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/create_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/create_column_query.nim new file mode 100644 index 0000000..7b6cb92 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/sqlite/sub/create_column_query.nim @@ -0,0 +1,409 @@ +## https://www.sqlite.org/lang_createtable.html#constraints + +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +# ============================================================================= +# int +# ============================================================================= +proc createSerialColumn(column:Column):string = + result = &"'{column.name}' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT" + + +proc createIntColumn(column:Column):string = + result = &"'{column.name}' INTEGER" + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "integer", column.name) + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + if column.isUnsigned: + result.add(&" CHECK ({column.name} >= 0)") + + +# ============================================================================= +# float +# ============================================================================= +proc createDecimalColumn(column:Column):string = + result = &"'{column.name}' NUMERIC" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + result.add(&" CHECK ({column.name} >= 0)") + + +proc createFloatColumn(column:Column):string = + result = &"'{column.name}' REAL" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultFloat}") + + if column.isUnsigned: + result.add(&" CHECK ({column.name} >= 0)") + + +# ============================================================================= +# char +# ============================================================================= +proc createUuidColumn(column:Column):string = + result = &"'{column.name}' VARCHAR UNIQUE" + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + let maxLength = column.info["maxLength"].getInt + result.add(sqliteLengthCheck(column, maxLength)) + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + +proc createCharColumn(column:Column):string = + result = &"'{column.name}' VARCHAR" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + let maxLength = column.info["maxLength"].getInt + result.add(sqliteLengthCheck(column, maxLength)) + + if column.isUnsigned: + notAllowedOption("unsigned", "char", column.name) + + +proc createVarcharColumn(column:Column):string = + result = &"'{column.name}' VARCHAR" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + let maxLength = column.info["maxLength"].getInt + result.add(sqliteLengthCheck(column, maxLength)) + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + +proc createTextColumn(column:Column):string = + result = &"'{column.name}' TEXT" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "text", column.name) + + +# ============================================================================= +# date +# ============================================================================= +proc createDateColumn(column:Column):string = + result = &"'{column.name}' DATE" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "date", column.name) + + +proc createDatetimeColumn(column:Column):string = + result = &"'{column.name}' DATETIME" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "datetime", column.name) + + +proc createTimeColumn(column:Column):string = + result = &"'{column.name}' TIME" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "time", column.name) + + +proc createTimestampColumn(column:Column):string = + result = &"'{column.name}' DATETIME" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT CURRENT_TIMESTAMP") + + if column.isUnsigned: + notAllowedOption("unsigned", "timestamp", column.name) + + +proc createTimestampsColumn(column:Column):string = + result = "'created_at' DATETIME DEFAULT CURRENT_TIMESTAMP, " + result.add("'updated_at' DATETIME DEFAULT CURRENT_TIMESTAMP") + + +proc createSoftDeleteColumn(column:Column):string = + result = "'deleted_at' DATETIME" + + +# ============================================================================= +# others +# ============================================================================= +proc createBlobColumn(column:Column):string = + result = &"'{column.name}' BLOB" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + +proc createBoolColumn(column:Column):string = + result = &"'{column.name}' TINYINT" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT {column.defaultBool}") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + +proc enumOptionsColumn(name:string, options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(" OR ") + optionsString.add( + &"{name} = '{option}'" + ) + + return optionsString + + +proc createEnumColumn(column:Column):string = + result = &"'{column.name}' VARCHAR" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultString}'") + + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + + let optionsString = enumOptionsColumn(column.name, options) + result.add(&" CHECK ({optionsString})") + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + +proc createJsonColumn(column:Column):string = + result = &"'{column.name}' JSON" + + if column.isUnique: + result.add(" UNIQUE") + + if not column.isNullable: + result.add(" NOT NULL") + + if column.isDefault: + result.add(&" DEFAULT '{column.defaultJson.pretty}'") + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + +# ============================================================================= +# foreign key +# ============================================================================= +proc createForeignColumn(column:Column):string = + result = &"'{column.name}' INTEGER" + if column.isDefault: + result.add(&" DEFAULT {column.defaultInt}") + + +proc createStrForeignColumn(column:Column):string = + result = &"'{column.name}' VARCHAR" + if column.isDefault: + result.add(&" DEFAULT {column.defaultString}") + + +proc createForeignKey(column:Column):string = + var onDeleteString = "RESTRICT" + if column.foreignOnDelete == CASCADE: + onDeleteString = "CASCADE" + elif column.foreignOnDelete == SET_NULL: + onDeleteString = "SET NULL" + elif column.foreignOnDelete == NO_ACTION: + onDeleteString = "NO ACTION" + + let tableName = column.info["table"].getStr + let columnnName = column.info["column"].getStr + return &"FOREIGN KEY('{column.name}') REFERENCES \"{tableName}\"('{columnnName}') ON DELETE {onDeleteString}" + + +proc createIndexColumn(column:Column, table:Table):string = + return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"('{column.name}')" + + +proc createColumnString*(column:Column):string = + case column.typ: + # int + of rdbIncrements: + return column.createSerialColumn() + of rdbInteger: + return column.createIntColumn() + of rdbSmallInteger: + return column.createIntColumn() + of rdbMediumInteger: + return column.createIntColumn() + of rdbBigInteger: + return column.createIntColumn() + # float + of rdbDecimal: + return column.createDecimalColumn() + of rdbDouble: + return column.createDecimalColumn() + of rdbFloat: + return column.createFloatColumn() + # char + of rdbUuid: + return column.createUuidColumn() + of rdbChar: + return column.createCharColumn() + of rdbString: + return column.createVarcharColumn() + # text + of rdbText: + return column.createTextColumn() + of rdbMediumText: + return column.createTextColumn() + of rdbLongText: + return column.createTextColumn() + # date + of rdbDate: + return column.createDateColumn() + of rdbDatetime: + return column.createDatetimeColumn() + of rdbTime: + return column.createTimeColumn() + of rdbTimestamp: + return column.createTimestampColumn() + of rdbTimestamps: + return column.createTimestampsColumn() + of rdbSoftDelete: + return column.createSoftDeleteColumn() + # others + of rdbBinary: + return column.createBlobColumn() + of rdbBoolean: + return column.createBoolColumn() + of rdbEnumField: + return column.createEnumColumn() + of rdbJson: + return column.createJsonColumn() + # foreign + of rdbForeign: + return column.createForeignColumn() + of rdbStrForeign: + return column.createStrForeignColumn() + + +proc createForeignString*(column:Column):string = + # if column.typ == rdbForeign or column.typ == rdbStrForeign: + return column.createForeignKey() + + +proc createIndexString*(table:Table, column:Column):string = + # if column.isIndex: + return column.createIndexColumn(table) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/add_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/add_column.nim new file mode 100644 index 0000000..ae6903f --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/add_column.nim @@ -0,0 +1,21 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import ../../models/table +import ../../models/column +import ./schema_utils +import ./surreal_query_type +import ./sub/create_column_query + + +proc addColumn*(self:SurrealSchema, isReset:bool) = + let queries = createColumnString(self.table, self.column) + + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/change_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/change_column.nim new file mode 100644 index 0000000..c0b42d7 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/change_column.nim @@ -0,0 +1,33 @@ +when NimMajor >= 2: + from db_connector/db_common import DbError +else: + from std/db_common import DbError + +# import checksums/sha1 +# import std/json +import std/strformat +# import ../../models/table +# import ../../models/column +# import ../../enums +# import ./schema_utils +import ./surreal_query_type +# import ./sub/change_column_query + + +proc changeColumn*(self:SurrealSchema, isReset:bool) = + ## SurrealDB not support change column field + ## + ## https://github.com/surrealdb/surrealdb/issues/1838 + + raise newException(DbError, &"SurrealDB not support change column field. see also: https://github.com/surrealdb/surrealdb/issues/1838") + + # var queries = changeColumnString(self.table, self.column) + + # if self.column.isIndex and self.column.typ != rdbIncrements: + # queries.add(changeIndexString(self.table, self.column)) + + # let schema = $self.column.toSchema() + # let checksum = $schema.secureHash() + + # if shouldRun(self.rdb, self.table, checksum, isReset): + # execThenSaveHistory(self.rdb, self.table.name, queries, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_migration_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_migration_table.nim new file mode 100644 index 0000000..0cb6835 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_migration_table.nim @@ -0,0 +1,39 @@ +## https://surrealdb.com/docs/surrealql/statements/define/table + +import std/asyncdispatch +import std/strformat +import std/strutils +import std/json +import ../../../query_builder/models/surreal/surreal_types +import ../../../query_builder/models/surreal/surreal_query +import ../../../query_builder/models/surreal/surreal_exec +import ../../models/table +import ./schema_utils +import ./surreal_query_type +import ./sub/create_column_query + + +proc createMigrationTable*(self: SurrealSchema, isReset: bool = false) = + let logDisplay = self.rdb.log.shouldDisplayLog + let logFile = self.rdb.log.shouldOutputLogFile + self.rdb.log.shouldDisplayLog = false + self.rdb.log.shouldOutputLogFile = false + defer: + self.rdb.log.shouldDisplayLog = logDisplay + self.rdb.log.shouldOutputLogFile = logFile + + let info = self.rdb.raw("INFO FOR DB").info().waitFor() + var hasMigrationTable = info[0]["result"]["tables"].contains("_allographer_migrations") + if isReset and hasMigrationTable: + self.rdb.raw("REMOVE TABLE `_allographer_migrations`").exec().waitFor() + hasMigrationTable = false + + if not hasMigrationTable: + var queries:seq[string] + queries.add(&"DEFINE TABLE `{self.table.name}` SCHEMAFULL") + + for i, column in self.table.columns: + queries.add(createColumnString(self.table, column)) + + let query = queries.join("; ") + exec(self.rdb, @[query]) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_sequence_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_sequence_table.nim new file mode 100644 index 0000000..dd36aa6 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_sequence_table.nim @@ -0,0 +1,32 @@ +import std/asyncdispatch +import std/strformat +import std/json +import ../../../query_builder/models/surreal/surreal_types +import ../../../query_builder/models/surreal/surreal_query +import ../../../query_builder/models/surreal/surreal_exec +import ../../models/table + + +proc createSequenceTable*(rdb:SurrealConnections) = + ## if _autoincrement_sequences table is not exists, create it + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + defer: + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + let info = rdb.raw(&"INFO FOR DB").info().waitFor() + if not info[0]["result"]["tables"].contains("_autoincrement_sequences"): + rdb.raw(&""" + DEFINE TABLE `_autoincrement_sequences`; + DEFINE FIELD `table` ON TABLE `_autoincrement_sequences` TYPE string; + DEFINE FIELD `column` ON TABLE `_autoincrement_sequences` TYPE string; + DEFINE FIELD `max_index` ON TABLE `_autoincrement_sequences` TYPE int; + DEFINE INDEX `_autoincrement_sequences_table_name_unique` ON TABLE `_autoincrement_sequences` COLUMNS `table`, `column` UNIQUE; + """).exec().waitFor() + + +proc resetSequence*(rdb:SurrealConnections, table:Table) = + rdb.table("_autoincrement_sequences").where("table", "=", table.name).delete().waitFor() diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_table.nim new file mode 100644 index 0000000..e4257f0 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/create_table.nim @@ -0,0 +1,29 @@ +## https://surrealdb.com/docs/surrealql/statements/define/table + +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/strformat +import std/strutils +import std/json +import ../../models/table +import ./schema_utils +import ./surreal_query_type +import ./sub/create_column_query + + +proc createTable*(self: SurrealSchema, isReset:bool) = + var queries:seq[string] + queries.add(&"DEFINE TABLE `{self.table.name}` SCHEMAFULL") + + for i, column in self.table.columns: + queries.add(createColumnString(self.table, column)) + + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + let query = queries.join("; ") + + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_column.nim new file mode 100644 index 0000000..7058f1b --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_column.nim @@ -0,0 +1,19 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import std/strformat +import ../../models/table +import ../../models/column +import ./schema_utils +import ./surreal_query_type + + +proc dropColumn*(self:SurrealSchema, isReset:bool) = + let query = &"REMOVE FIELD `{self.column.name}` ON TABLE `{self.table.name}`" + let schema = $self.column.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_table.nim new file mode 100644 index 0000000..3baa72d --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/drop_table.nim @@ -0,0 +1,18 @@ +when NimMajor >= 2: + import checksums/sha1 +else: + import std/sha1 + +import std/json +import std/strformat +import ../../models/table +import ./schema_utils +import ./surreal_query_type + + +proc dropTable*(self:SurrealSchema, isReset:bool) = + let query = &"REMOVE TABLE `{self.table.name}`" + let schema = $self.table.toSchema() + let checksum = $schema.secureHash() + if shouldRun(self.rdb, self.table, checksum, isReset): + execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_column.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_column.nim new file mode 100644 index 0000000..0d4c73d --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_column.nim @@ -0,0 +1,26 @@ +when NimMajor >= 2: + from db_connector/db_common import DbError +else: + from std/db_common import DbError + +import std/strformat +# import checksums/sha1 +# import std/json +# import ../../models/table +# import ../../models/column +# import ./schema_utils +import ./surreal_query_type + + +proc renameColumn*(self:SurrealSchema, isReset:bool) = + ## SurrealDB not support rename column field + ## + ## https://github.com/surrealdb/surrealdb/issues/1615 + + raise newException(DbError, &"SurrealDB not support rename column field. see also: https://github.com/surrealdb/surrealdb/issues/1615") + + # let query = &"ALTER TABLE \"{self.table.name}\" RENAME COLUMN \"{self.column.previousName}\" TO \"{self.column.name}\"" + # let schema = $self.column.toSchema() + # let checksum = $schema.secureHash() + # if shouldRun(self.rdb, self.table, checksum, isReset): + # execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_table.nim new file mode 100644 index 0000000..80c9297 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/rename_table.nim @@ -0,0 +1,27 @@ +when NimMajor >= 2: + from db_connector/db_common import DbError +else: + from std/db_common import DbError + +import std/strformat +# import checksums/sha1 +# import std/json +# import ../../models/table +import ./surreal_query_type +# import ./schema_utils + + +proc renameTable*(self:SurrealSchema, isReset:bool) = + ## SurrealDB not support rename table field + ## + ## https://github.com/surrealdb/surrealdb/issues/1615 + + raise newException(DbError, &"SurrealDB not support rename table field. see also: https://github.com/surrealdb/surrealdb/issues/1615") + + + # let query = &"ALTER TABLE \"{self.table.previousName}\" RENAME TO \"{self.table.name}\"" + # let schema = $self.table.toSchema() + # let checksum = $schema.secureHash() + + # if shouldRun(self.rdb, self.table, checksum, isReset): + # execThenSaveHistory(self.rdb, self.table.name, query, checksum) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/reset_table.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/reset_table.nim new file mode 100644 index 0000000..a9374fc --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/reset_table.nim @@ -0,0 +1,13 @@ +import std/asyncdispatch +import std/strformat +import ../../../query_builder/models/surreal/surreal_query +import ../../../query_builder/models/surreal/surreal_exec +import ../../models/table +import ./surreal_query_type + + +proc resetMigrationTable*(self:SurrealSchema) = + self.rdb.table("_allographer_migrations").where("name", "=", self.table.name).delete().waitFor + +proc resetTable*(self:SurrealSchema) = + self.rdb.raw(&"REMOVE TABLE `{self.table.name}`").exec().waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/schema_utils.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/schema_utils.nim new file mode 100644 index 0000000..d857663 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/schema_utils.nim @@ -0,0 +1,129 @@ +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import std/strutils +import std/times +import ../../../query_builder/models/surreal/surreal_types +import ../../../query_builder/models/surreal/surreal_query +import ../../../query_builder/models/surreal/surreal_exec +import ../../../query_builder/error +import ../../models/table + + +proc notAllowedOption*(option, typ, column:string) = + ## {option} is not allowed in {typ} column {column} + raise newException(DbError, &"{option} is not allowed in {typ} column {column}") + +proc notAllowedType*(typ:string) = + ## Change to {typ} type is not allowed + raise newException(DbError, &"type {typ} is not allowed") + + +proc responseMessage(node: JsonNode): string = + if node.kind == JObject: + for key in ["detail", "information", "result", "message", "error"]: + if node.hasKey(key): + let value = node[key] + if value.kind == JString: + return value.getStr() + return value.pretty + return node.pretty + + +# ================================================== +# SurrealDB +# ================================================== + +proc shouldRun*(rdb:SurrealConnections, table:Table, checksum:string, isReset:bool):bool = + if isReset: + return true + + let history = rdb.table("_allographer_migrations") + .where("checksum", "=", checksum) + .first() + .waitFor + return not history.isSome() or not history.get()["status"].getBool + + +proc exec*(rdb:SurrealConnections, queries:seq[string]) = + var isSuccess = false + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + defer: + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + try: + for query in queries: + rdb.raw(query).exec().waitFor + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + +proc execThenSaveHistory*(rdb:SurrealConnections, tableName:string, queries:seq[string], checksum:string) = + var isSuccess = false + + try: + for query in queries: + let resp = rdb.raw(query).info().waitFor + for row in resp: + if row.hasKey("status") and row["status"].kind == JString and row["status"].getStr != "OK": + raise newException(DbError, responseMessage(row)) + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + defer: + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + let tableQuery = queries.join("; ") + let createdAt = now().utc.format("yyyy-MM-dd'T'HH:mm:ss'.'fff'Z'") + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": tableQuery, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor + + +proc execThenSaveHistory*(rdb:SurrealConnections, tableName:string, query:string, checksum:string) = + var isSuccess = false + try: + let resp = rdb.raw(query).info().waitFor + if resp.kind == JObject: + raise newException(DbError, responseMessage(resp)) + for row in resp: + if row.hasKey("status") and row["status"].kind == JString and row["status"].getStr != "OK": + raise newException(DbError, responseMessage(row)) + isSuccess = true + except DbError: + echo getCurrentExceptionMsg() + + let logDisplay = rdb.log.shouldDisplayLog + let logFile = rdb.log.shouldOutputLogFile + rdb.log.shouldDisplayLog = false + rdb.log.shouldOutputLogFile = false + defer: + rdb.log.shouldDisplayLog = logDisplay + rdb.log.shouldOutputLogFile = logFile + + let createdAt = now().utc.format("yyyy-MM-dd'T'HH:mm:ss'.'fff'Z'") + rdb.table("_allographer_migrations").insert(%*{ + "name": tableName, + "query": query, + "checksum": checksum, + "created_at": createdAt, + "status": isSuccess + }) + .waitFor diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/sub/create_column_query.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/sub/create_column_query.nim new file mode 100644 index 0000000..1748146 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/sub/create_column_query.nim @@ -0,0 +1,606 @@ +## https://surrealdb.com/docs/surrealql/statements/define/field +## https://surrealdb.com/docs/surrealql/statements/define/indexes +## https://surrealdb.com/docs/surrealql/datamodel/ids +## https://surrealdb.com/docs/surrealql/datamodel/simple +## https://surrealdb.com/docs/surrealql/datamodel/numbers +## https://surrealdb.com/docs/surrealql/datamodel/strings + +import std/json +import std/strformat +import ../../../enums +import ../../../models/table +import ../../../models/column +import ../schema_utils + + +proc addAssertPart(assertClause: var string, part: string) = + if part.len == 0: + return + + if assertClause.len > 0: + assertClause.add(" AND ") + assertClause.add(part) + + +proc addValueClause(query: var string, defaultClause: string, isNullable: bool, fallbackClause: string) = + if defaultClause.len > 0: + query.add(&" VALUE $value OR {defaultClause}") + elif isNullable: + query.add(" VALUE $value OR NONE") + else: + query.add(&" VALUE $value OR {fallbackClause}") + + +proc addAssertClause(query: var string, assertBody: string, isNullable: bool) = + if assertBody.len == 0: + if not isNullable: + query.add(" ASSERT $value != NONE") + return + + if isNullable: + query.add(&" ASSERT $value = NONE OR ({assertBody})") + else: + query.add(&" ASSERT {assertBody}") + + +proc autoIncrementValueExpr(table: Table, column: Column): string = + &"(SELECT `max_index` FROM `_autoincrement_sequences` WHERE `table` = \"{table.name}\" AND `column` = \"{column.name}\" LIMIT 1)[0].max_index + 1" + + +proc isSurrealIdField(column: Column): bool = + column.name == "id" + + +# ============================================================================= +# int +# ============================================================================= +proc createIncrementsColumn(column:Column, table:Table):seq[string] = + if isSurrealIdField(column): + return @[] + + let nextIndexExpr = autoIncrementValueExpr(table, column) + result.add(&""" + INSERT INTO `_autoincrement_sequences` {{table: "{table.name}", column: "{column.name}", max_index: 0}}; + DEFINE EVENT `autoincrement_{table.name}_{column.name}` ON TABLE `{table.name}` WHEN $event = "CREATE" THEN {{ + UPDATE `_autoincrement_sequences` MERGE {{max_index: $after.{column.name}}} WHERE `table` = "{table.name}" AND `column` = "{column.name}"; + }} + """) + result.add(&"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE int VALUE $value OR {nextIndexExpr}") + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + +proc createIntColumn(column:Column, table:Table):seq[string] = + var query = "" + var assertClause = "" + + if column.isAutoIncrement: + if isSurrealIdField(column): + return @[] + + let nextIndexExpr = autoIncrementValueExpr(table, column) + query.add(&""" + INSERT INTO `_autoincrement_sequences` {{table: "{table.name}", column: "{column.name}", max_index: 0}}; + DEFINE EVENT `autoincrement_{table.name}_{column.name}` ON TABLE `{table.name}` WHEN $event = "CREATE" THEN {{ + UPDATE `_autoincrement_sequences` MERGE {{max_index: $after.{column.name}}} WHERE `table` = "{table.name}" AND `column` = "{column.name}"; + }}; + """) + + query.add(&"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE int VALUE $value OR {nextIndexExpr}") + return @[query] + + query.add(&"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE int") + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isUnsigned: + addAssertPart(assertClause, "$value >= 0") + + if column.isDefault: + addValueClause(query, $column.defaultInt, column.isNullable, "0") + else: + addValueClause(query, "", column.isNullable, "0") + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + +# ============================================================================= +# float +# ============================================================================= +proc createDecimalColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE decimal" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isUnsigned: + addAssertPart(assertClause, "$value >= 0") + + if column.isDefault: + addValueClause(query, $column.defaultFloat, column.isNullable, "0.0") + else: + addValueClause(query, "", column.isNullable, "0.0") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + +proc createFloatColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE float" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isUnsigned: + addAssertPart(assertClause, "$value >= 0") + + if column.isDefault: + addValueClause(query, $column.defaultFloat, column.isNullable, "0.0") + else: + addValueClause(query, "", column.isNullable, "0.0") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + +# ============================================================================= +# char +# ============================================================================= +proc createUuidColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE string" + if column.isNullable: + query.add(" | NONE") + query.add(" VALUE $value OR rand::uuid()") + addAssertClause(query, "", column.isNullable) + result.add(query) + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + +proc createCharColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE string" + if column.isNullable: + query.add(" | NONE") + let maxLength = column.info["maxLength"].getInt + var assertClause = &"string::len($value) < {maxLength}" + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, &"'{column.defaultString}'", column.isNullable, "''") + else: + addValueClause(query, "", column.isNullable, "''") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + +proc createVarcharColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE string" + if column.isNullable: + query.add(" | NONE") + let maxLength = column.info["maxLength"].getInt + var assertClause = &"string::len($value) < {maxLength}" + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, &"'{column.defaultString}'", column.isNullable, "''") + else: + addValueClause(query, "", column.isNullable, "''") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + +proc createTextColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE string" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, &"'{column.defaultString}'", column.isNullable, "''") + else: + addValueClause(query, "", column.isNullable, "''") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + +# ============================================================================= +# date +# ============================================================================= +proc createDatetimeColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE datetime" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault and column.defaultDatetime == Current: + query.add(&" VALUE $value OR time::now()") + elif column.isDefault and column.defaultDatetime == CurrentOnUpdate: + query.add(&" VALUE time::now()") + elif column.isNullable: + query.add(" VALUE $value OR NULL") + else: + query.add(" VALUE $value OR \"1970-01-01T00:00:00Z\"") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "varchar", column.name) + + +proc createTimestampsColumn(column:Column, table:Table):seq[string] = + result.add(&"DEFINE FIELD `created_at` ON TABLE `{table.name}` TYPE datetime VALUE $value OR time::now()") + result.add(&"DEFINE INDEX `{table.name}_created_at_index` ON TABLE `{table.name}` COLUMNS `created_at`") + + result.add(&"DEFINE FIELD `updated_at` ON TABLE `{table.name}` TYPE datetime VALUE time::now()") + result.add(&"DEFINE INDEX `{table.name}_updated_at_index` ON TABLE `{table.name}` COLUMNS `updated_at`") + + +proc createSoftDeleteColumn(column:Column, table:Table):seq[string] = + result.add(&"DEFINE FIELD `deleted_at` ON TABLE `{table.name}` TYPE datetime") + result.add(&"DEFINE INDEX `{table.name}_deleted_at_index` ON TABLE `{table.name}` COLUMNS `deleted_at`") + + +# ============================================================================= +# others +# ============================================================================= +proc createBlobColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE string" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, &"'{column.defaultString}'", column.isNullable, "''") + else: + addValueClause(query, "", column.isNullable, "''") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "blob", column.name) + + +proc createBoolColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE bool" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, $column.defaultBool, column.isNullable, "false") + else: + addValueClause(query, "", column.isNullable, "false") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "bool", column.name) + + +proc enumOptionsColumn(options:seq[string]):string = + var optionsString = "" + for i, option in options: + if i > 0: optionsString.add(", ") + optionsString.add( + &"'{option}'" + ) + + return optionsString + + +proc createEnumColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE string" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + var options:seq[string] + for row in column.info["options"].items: + options.add(row.getStr) + let optionsString = enumOptionsColumn(options) + addAssertPart(assertClause, &"$value INSIDE [{optionsString}]") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, &"'{column.defaultString}'", column.isNullable, "''") + else: + let default = column.info["options"][0].getStr + addValueClause(query, &"'{default}'", column.isNullable, "''") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "enum", column.name) + + +proc createJsonColumn(column:Column, table:Table):seq[string] = + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE object FLEXIBLE" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, &"{$column.defaultJson}", column.isNullable, "{}") + else: + addValueClause(query, "", column.isNullable, "{}") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "json", column.name) + + +# ============================================================================= +# foreign key +# ============================================================================= +proc createForeignColumn(column:Column, table:Table):seq[string] = + let refTable = column.info["table"].getStr + var query = &"DEFINE FIELD `{column.name}` ON TABLE `{table.name}` TYPE record<{refTable}>" + var assertClause = "" + if column.isNullable: + query.add(" | NONE") + + if not column.isNullable: + addAssertPart(assertClause, "$value != NONE") + + if column.isDefault: + addValueClause(query, &"{column.defaultString}", column.isNullable, "NULL") + + if column.isAutoIncrement: + notAllowedOption("autoincrement", "decimal", column.name) + elif column.isNullable: + addValueClause(query, "", true, "NULL") + + addAssertClause(query, assertClause, column.isNullable) + result.add(query) + + if column.isIndex: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_index` ON TABLE `{table.name}` COLUMNS `{column.name}`") + + if column.isUnique: + result.add(&"DEFINE INDEX `{table.name}_{column.name}_unique` ON TABLE `{table.name}` COLUMNS `{column.name}` UNIQUE") + + if column.isUnsigned: + notAllowedOption("unsigned", "foreign", column.name) + + +# proc createStrForeignColumn(column:Column, table:Table):string = +# result = &"'{column.name}' VARCHAR" +# if column.isDefault: +# result.add(&" DEFAULT {column.defaultString}") + + +# proc createForeignKey(column:Column):string = +# var onDeleteString = "RESTRICT" +# if column.foreignOnDelete == CASCADE: +# onDeleteString = "CASCADE" +# elif column.foreignOnDelete == SET_NULL: +# onDeleteString = "SET NULL" +# elif column.foreignOnDelete == NO_ACTION: +# onDeleteString = "NO ACTION" + +# let tableName = column.info["table"].getStr +# let columnnName = column.info["column"].getStr +# return &"FOREIGN KEY('{column.name}') REFERENCES \"{tableName}\"('{columnnName}') ON DELETE {onDeleteString}" + + +# proc createIndexColumn(column:Column, table:Table):string = +# return &"CREATE INDEX IF NOT EXISTS \"{table.name}_{column.name}_index\" ON \"{table.name}\"('{column.name}')" + + +proc createColumnString*(table:Table, column:Column):seq[string] = + case column.typ: + # int + of rdbIncrements: + return column.createIncrementsColumn(table) + of rdbInteger: + return column.createIntColumn(table) + of rdbSmallInteger: + return column.createIntColumn(table) + of rdbMediumInteger: + return column.createIntColumn(table) + of rdbBigInteger: + return column.createIntColumn(table) + # float + of rdbDecimal: + return column.createDecimalColumn(table) + of rdbDouble: + return column.createDecimalColumn(table) + of rdbFloat: + return column.createFloatColumn(table) + # char + of rdbUuid: + return column.createUuidColumn(table) + of rdbChar: + return column.createCharColumn(table) + of rdbString: + return column.createVarcharColumn(table) + # text + of rdbText: + return column.createTextColumn(table) + of rdbMediumText: + return column.createTextColumn(table) + of rdbLongText: + return column.createTextColumn(table) + # date + of rdbDate: + return column.createDatetimeColumn(table) + of rdbDatetime: + return column.createDatetimeColumn(table) + of rdbTime: + notAllowedType("time") + # return column.createDatetimeColumn(table) + of rdbTimestamp: + return column.createDatetimeColumn(table) + of rdbTimestamps: + return column.createTimestampsColumn(table) + of rdbSoftDelete: + return column.createSoftDeleteColumn(table) + # # others + of rdbBinary: + return column.createBlobColumn(table) + of rdbBoolean: + return column.createBoolColumn(table) + of rdbEnumField: + return column.createEnumColumn(table) + of rdbJson: + return column.createJsonColumn(table) + # foreign + of rdbForeign: + return column.createForeignColumn(table) + of rdbStrForeign: + return column.createForeignColumn(table) + # else: + # discard + + +# proc createForeignString*(column:Column):string = +# # if column.typ == rdbForeign or column.typ == rdbStrForeign: +# return column.createForeignKey() + + +# proc createIndexString*(table:Table, column:Column):string = +# # if column.isIndex: +# return column.createIndexColumn(table) diff --git a/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/surreal_query_type.nim b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/surreal_query_type.nim new file mode 100644 index 0000000..4823950 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/queries/surreal/surreal_query_type.nim @@ -0,0 +1,15 @@ +import ../../../query_builder/models/surreal/surreal_types +import ../../models/table +import ../../models/column + + +type SurrealSchema* = ref object + rdb*:SurrealConnections + table*:Table + column*:Column + +proc new*(_:type SurrealSchema, rdb:SurrealConnections, table:Table):SurrealSchema = + return SurrealSchema(rdb:rdb, table:table) + +proc new*(_:type SurrealSchema, rdb:SurrealConnections, table:Table, column:Column):SurrealSchema = + return SurrealSchema(rdb:rdb, table:table, column:column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/alter.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/alter.nim new file mode 100644 index 0000000..7500724 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/alter.nim @@ -0,0 +1,45 @@ +import std/os +import ../../../query_builder/models/baradb/baradb_types +import ../../models/table +import ../../models/column +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/baradb/create_migration_table +import ../../queries/baradb/add_column +import ../../queries/baradb/change_column +import ../../queries/baradb/rename_column +import ../../queries/baradb/drop_column +import ../../queries/baradb/rename_table + +proc alter*(rdb: BaradbConnections, tables: varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + for i, table in tables: + table.usecaseType = Alter + case table.migrationType + of CreateTable: + for column in table.columns: + column.usecaseType = Alter + case column.migrationType + of AddColumn: + query = createSchema(rdb, table, column) + query.addColumn(isReset) + of ChangeColumn: + query = createSchema(rdb, table, column) + query.changeColumn(isReset) + of RenameColumn: + query = createSchema(rdb, table, column) + query.renameColumn(isReset) + of DropColumn: + query = createSchema(rdb, table, column) + query.dropColumn(isReset) + of ChangeTable: + discard + of RenameTable: + query = createSchema(rdb, table) + query.renameTable(isReset) + of DropTable: + discard diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create.nim new file mode 100644 index 0000000..bdebbb6 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create.nim @@ -0,0 +1,24 @@ +import std/os +import ../../../query_builder/models/baradb/baradb_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/baradb/create_migration_table +import ../../queries/baradb/reset_table +import ../../queries/baradb/create_table + +proc create*(rdb: BaradbConnections, tables: varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + if isReset: + for i in countdown(tables.len-1, 0): + let table = tables[i] + query = createSchema(rdb, table) + query.resetTable() + for table in tables: + table.usecaseType = Create + query = createSchema(rdb, table) + query.createTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_query_def.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_query_def.nim new file mode 100644 index 0000000..68c5077 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_query_def.nim @@ -0,0 +1,10 @@ +import ../../../query_builder/models/baradb/baradb_types +import ../../queries/baradb/baradb_query_type +import ../../models/table +import ../../models/column + +proc createSchema*(rdb: BaradbConnections, table: Table): BaradbSchema = + return BaradbSchema.new(rdb, table) + +proc createSchema*(rdb: BaradbConnections, table: Table, column: Column): BaradbSchema = + return BaradbSchema.new(rdb, table, column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_schema.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_schema.nim new file mode 100644 index 0000000..018754d --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/create_schema.nim @@ -0,0 +1,60 @@ +import std/asyncdispatch +import std/strutils +import std/strformat +import std/json +import std/tables +import std/os +import ../../../utils/snake_to_camel +import ../../../query_builder/models/baradb/baradb_types +import ../../../query_builder/models/baradb/baradb_query +import ../../../query_builder/models/baradb/baradb_exec + +proc getTableInfo(rdb: BaradbConnections): Future[Table[string, seq[tuple[name: string, typ: string]]]] {.async.} = + var tablesInfo = initTable[string, seq[tuple[name: string, typ: string]]]() + let tables = await rdb.raw("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'").get() + for table in tables: + let tableName = table["table_name"].getStr() + let query = "SELECT column_name, data_type FROM information_schema.columns WHERE table_name = ? ORDER BY ordinal_position" + let columns = await rdb.raw(query, %*[tableName]).get() + var columnInfo: seq[tuple[name: string, typ: string]] + for col in columns: + columnInfo.add((name: col["column_name"].getStr(), typ: col["data_type"].getStr())) + tablesInfo[tableName] = columnInfo + return tablesInfo + +proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: string]]]): string = + var code = "import std/json" + for tableName, columns in tablesInfo.pairs: + if tableName == "_allographer_migrations": + continue + let tableNameCamel = tableName.snakeToCamel() + code.add(&"\n\ntype {tableNameCamel}Table* = object\n") + code.add(&" ## {tableName}\n") + for col in columns: + let nimType = + case col.typ.toLower() + of "smallint", "integer", "bigint", "serial": + "int" + of "character", "character varying", "text", "date", "timestamp without time zone", "time without time zone", "bytea", "varchar": + "string" + of "boolean": + "bool" + of "numeric", "double precision", "real": + "float" + of "json", "jsonb": + "JsonNode" + else: + "string" + code.add(&" {col.name}*: {nimType}\n") + return code + +proc createSchema*(rdb: BaradbConnections, schemaPath = "") {.async.} = + try: + let tablesInfo = await rdb.getTableInfo() + let schemaCode = generateSchemaCode(tablesInfo) + let schemaFilePath = if schemaPath == "": getCurrentDir() / "schema.nim" else: schemaPath + writeFile(schemaFilePath, schemaCode) + echo "schema generated successfully in ", schemaFilePath + except Exception as e: + echo "Error generating schema: ", e.msg + raise diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/drop.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/drop.nim new file mode 100644 index 0000000..23590aa --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/baradb/drop.nim @@ -0,0 +1,18 @@ +import std/os +import ../../../query_builder/models/baradb/baradb_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/baradb/create_migration_table +import ../../queries/baradb/drop_table + +proc drop*(rdb: BaradbConnections, tables: varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + for table in tables: + table.usecaseType = Drop + query = createSchema(rdb, table) + query.dropTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/alter.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/alter.nim new file mode 100644 index 0000000..8223e3e --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/alter.nim @@ -0,0 +1,49 @@ +import std/os +import ../../../query_builder/models/mariadb/mariadb_types +import ../../models/table +import ../../models/column +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/mariadb/create_migration_table +import ../../queries/mariadb/add_column +import ../../queries/mariadb/change_column +import ../../queries/mariadb/rename_column +import ../../queries/mariadb/drop_column +import ../../queries/mariadb/rename_table + + +proc alter*(rdb:MariadbConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for i, table in tables: + table.usecaseType = Alter + case table.migrationType + of CreateTable: + for column in table.columns: + column.usecaseType = Alter + case column.migrationType + of AddColumn: + query = createSchema(rdb, table, column) + query.addColumn(isReset) + of ChangeColumn: + query = createSchema(rdb, table, column) + query.changeColumn(isReset) + of RenameColumn: + query = createSchema(rdb, table, column) + query.renameColumn(isReset) + of DropColumn: + query = createSchema(rdb, table, column) + query.dropColumn(isReset) + of ChangeTable: + discard + of RenameTable: + query = createSchema(rdb, table) + query.renameTable(isReset) + of DropTable: + discard diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create.nim new file mode 100644 index 0000000..5f19422 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create.nim @@ -0,0 +1,31 @@ +import std/os +import ../../../query_builder/models/mariadb/mariadb_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/mariadb/create_migration_table +import ../../queries/mariadb/create_table +import ../../queries/mariadb/reset_table + + +proc create*(rdb:MariadbConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + if isReset: + # delete table in reverse loop in tables + for i in countdown(tables.len-1, 0): + let table = tables[i] + query = createSchema(rdb, table) + query.resetMigrationTable() + query.resetTable() + + for table in tables: + table.usecaseType = Create + query = createSchema(rdb, table) + query.createTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_query_def.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_query_def.nim new file mode 100644 index 0000000..dc55819 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_query_def.nim @@ -0,0 +1,11 @@ +import ../../../query_builder/models/mariadb/mariadb_types +import ../../queries/mariadb/mariadb_query_type +import ../../models/table +import ../../models/column + + +proc createSchema*(rdb:MariadbConnections, table:Table):MariadbSchema = + return MariadbSchema.new(rdb, table) + +proc createSchema*(rdb:MariadbConnections, table:Table, column:Column):MariadbSchema = + return MariadbSchema.new(rdb, table, column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_schema.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_schema.nim new file mode 100644 index 0000000..1ac8fe2 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/create_schema.nim @@ -0,0 +1,98 @@ +import std/asyncdispatch +import std/strutils +import std/strformat +import std/json +import std/tables +import std/re +import std/os +import ../../../utils/snake_to_camel +import ../../../query_builder/models/mariadb/mariadb_types +import ../../../query_builder/models/mariadb/mariadb_query +import ../../../query_builder/models/mariadb/mariadb_exec + + +proc getTableInfo(rdb: MariaDBConnections): Future[Table[string, seq[tuple[name: string, typ: string]]]] {.async.} = + ## get table info + var tablesInfo = initTable[string, seq[tuple[name: string, typ: string]]]() + + # get table list + let tables = await rdb.raw( + "SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE()" + ).get() + + for table in tables: + let tableName = table["table_name"].getStr() + + if tableName == "_allographer_migrations": + continue + + # get column info + let query = + """SELECT column_name, data_type + FROM information_schema.columns + WHERE table_name = ? + AND table_schema = DATABASE() + ORDER BY ordinal_position + """ + .replace(re"\s{2,}", " ") + + let columns = rdb.raw(query, %*[tableName]).get().await + + var columnInfo: seq[tuple[name: string, typ: string]] + for col in columns: + columnInfo.add(( + name: col["column_name"].getStr(), + typ: col["data_type"].getStr() + )) + + tablesInfo[tableName] = columnInfo + + return tablesInfo + +proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: string]]]): string = + ## generate schema.nim code + var code = "import std/json" + + for tableName, columns in tablesInfo.pairs: + let tableNameCamel = tableName.snakeToCamel() + code.add("\n\n") + code.add(&"type {tableNameCamel}Table* = object\n") + code.add(&" ## {tableName}\n") + for col in columns: + let nimType = + case col.typ.toLower() + of "tinyint": + "bool" + of "smallint", "mediumint", "int", "bigint": + "int" + of "char", "varchar", "tinytext", "text", "mediumtext", "longtext", "blob", "enum", "date", "datetime", "time", "timestamp": + "string" + of "float", "double", "decimal": + "float" + of "json": + "JsonNode" + else: + "string" # default + code.add &" {col.name}*: {nimType}\n" + + return code + +proc createSchema*(rdb: MariaDBConnections, schemaPath="") {.async.} = + ## if schemaPath is not specified, the schema will be generated in the current directory + ## + ## Default schema file name is `getCurrentDir() / "schema.nim"` + try: + let tablesInfo = rdb.getTableInfo().await + let schemaCode = generateSchemaCode(tablesInfo) + + let schemaFilePath = + if schemaPath == "": + getCurrentDir() / "schema.nim" + else: + schemaPath + + writeFile(schemaFilePath, schemaCode) + echo "schema generated successfully in ", schemaFilePath + except Exception as e: + echo "Error generating schema: ", e.msg + raise diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/drop.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/drop.nim new file mode 100644 index 0000000..2afdf0b --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mariadb/drop.nim @@ -0,0 +1,22 @@ +import std/os +import ../../../query_builder/models/mariadb/mariadb_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/mariadb/create_migration_table +import ../../queries/mariadb/drop_table + + +proc drop*(rdb:MariadbConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for table in tables: + table.usecaseType = Drop + query = createSchema(rdb, table) + query.dropTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/alter.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/alter.nim new file mode 100644 index 0000000..61b7950 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/alter.nim @@ -0,0 +1,53 @@ +import std/os +import ../../../query_builder/models/mysql/mysql_types +import ../../models/table +import ../../models/column +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/mysql/create_migration_table +import ../../queries/mysql/add_column +import ../../queries/mysql/change_column +import ../../queries/mysql/rename_column +import ../../queries/mysql/drop_column +import ../../queries/mysql/rename_table + + +proc alter*(rdb:MysqlConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for i, table in tables: + table.usecaseType = Alter + case table.migrationType + of CreateTable: + for column in table.columns: + column.usecaseType = Alter + case column.migrationType + of AddColumn: + discard + query = createSchema(rdb, table, column) + query.addColumn(isReset) + of ChangeColumn: + discard + query = createSchema(rdb, table, column) + query.changeColumn(isReset) + of RenameColumn: + discard + query = createSchema(rdb, table, column) + query.renameColumn(isReset) + of DropColumn: + discard + query = createSchema(rdb, table, column) + query.dropColumn(isReset) + of ChangeTable: + discard + of RenameTable: + query = createSchema(rdb, table) + query.renameTable(isReset) + of DropTable: + discard diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create.nim new file mode 100644 index 0000000..4003e55 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create.nim @@ -0,0 +1,31 @@ +import std/os +import ../../enums +import ../../../query_builder/models/mysql/mysql_types +import ../../models/table +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/mysql/create_migration_table +import ../../queries/mysql/create_table +import ../../queries/mysql/reset_table + + +proc create*(rdb:MysqlConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + if isReset: + # delete table in reverse loop in tables + for i in countdown(tables.len-1, 0): + let table = tables[i] + query = createSchema(rdb, table) + query.resetMigrationTable() + query.resetTable() + + for table in tables: + table.usecaseType = Create + query = createSchema(rdb, table) + query.createTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_query_def.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_query_def.nim new file mode 100644 index 0000000..fcbf403 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_query_def.nim @@ -0,0 +1,11 @@ +import ../../../query_builder/models/mysql/mysql_types +import ../../queries/mysql/mysql_query_type +import ../../models/table +import ../../models/column + + +proc createSchema*(rdb:MysqlConnections, table:Table):MysqlSchema = + return MysqlSchema.new(rdb, table) + +proc createSchema*(rdb:MysqlConnections, table:Table, column:Column):MysqlSchema = + return MysqlSchema.new(rdb, table, column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_schema.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_schema.nim new file mode 100644 index 0000000..33cb00e --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/create_schema.nim @@ -0,0 +1,98 @@ +import std/asyncdispatch +import std/strutils +import std/strformat +import std/json +import std/tables +import std/re +import std/os +import ../../../utils/snake_to_camel +import ../../../query_builder/models/mysql/mysql_types +import ../../../query_builder/models/mysql/mysql_query +import ../../../query_builder/models/mysql/mysql_exec + + +proc getTableInfo(rdb: MysqlConnections): Future[Table[string, seq[tuple[name: string, typ: string]]]] {.async.} = + ## get table info + var tablesInfo = initTable[string, seq[tuple[name: string, typ: string]]]() + + # get table list + let tables = await rdb.raw( + "SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE()" + ).get() + + for table in tables: + let tableName = table["TABLE_NAME"].getStr() + + if tableName == "_allographer_migrations": + continue + + # get column info + let query = + """SELECT column_name, data_type + FROM information_schema.columns + WHERE table_name = ? + AND table_schema = DATABASE() + ORDER BY ordinal_position + """ + .replace(re"\s{2,}", " ") + + let columns = rdb.raw(query, %*[tableName]).get().await + + var columnInfo: seq[tuple[name: string, typ: string]] + for col in columns: + columnInfo.add(( + name: col["COLUMN_NAME"].getStr(), + typ: col["DATA_TYPE"].getStr() + )) + + tablesInfo[tableName] = columnInfo + + return tablesInfo + +proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: string]]]): string = + ## generate schema.nim code + var code = "import std/json" + + for tableName, columns in tablesInfo.pairs: + let tableNameCamel = tableName.snakeToCamel() + code.add("\n\n") + code.add(&"type {tableNameCamel}Table* = object\n") + code.add(&" ## {tableName}\n") + for col in columns: + let nimType = + case col.typ.toLower() + of "tinyint": + "bool" + of "smallint", "mediumint", "int", "bigint": + "int" + of "char", "varchar", "tinytext", "text", "mediumtext", "longtext", "blob", "enum", "date", "datetime", "time", "timestamp": + "string" + of "float", "double", "decimal": + "float" + of "json": + "JsonNode" + else: + "string" # default + code.add &" {col.name}*: {nimType}\n" + + return code + +proc createSchema*(rdb: MysqlConnections, schemaPath="") {.async.} = + ## if schemaPath is not specified, the schema will be generated in the current directory + ## + ## Default schema file name is `getCurrentDir() / "schema.nim"` + try: + let tablesInfo = rdb.getTableInfo().await + let schemaCode = generateSchemaCode(tablesInfo) + + let schemaFilePath = + if schemaPath == "": + getCurrentDir() / "schema.nim" + else: + schemaPath + + writeFile(schemaFilePath, schemaCode) + echo "schema generated successfully in ", schemaFilePath + except Exception as e: + echo "Error generating schema: ", e.msg + raise diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/drop.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/drop.nim new file mode 100644 index 0000000..c846d00 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/mysql/drop.nim @@ -0,0 +1,22 @@ +import std/os +import ../../../query_builder/models/mysql/mysql_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/mysql/create_migration_table +import ../../queries/mysql/drop_table + + +proc drop*(rdb:MysqlConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for table in tables: + table.usecaseType = Drop + query = createSchema(rdb, table) + query.dropTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/alter.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/alter.nim new file mode 100644 index 0000000..9a75442 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/alter.nim @@ -0,0 +1,53 @@ +import std/os +import ../../../query_builder/models/postgres/postgres_types +import ../../models/table +import ../../models/column +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/postgres/create_migration_table +import ../../queries/postgres/add_column +import ../../queries/postgres/change_column +import ../../queries/postgres/rename_column +import ../../queries/postgres/drop_column +import ../../queries/postgres/rename_table + + +proc alter*(rdb:PostgresConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for i, table in tables: + table.usecaseType = Alter + case table.migrationType + of CreateTable: + for column in table.columns: + column.usecaseType = Alter + case column.migrationType + of AddColumn: + discard + query = createSchema(rdb, table, column) + query.addColumn(isReset) + of ChangeColumn: + discard + query = createSchema(rdb, table, column) + query.changeColumn(isReset) + of RenameColumn: + discard + query = createSchema(rdb, table, column) + query.renameColumn(isReset) + of DropColumn: + discard + query = createSchema(rdb, table, column) + query.dropColumn(isReset) + of ChangeTable: + discard + of RenameTable: + query = createSchema(rdb, table) + query.renameTable(isReset) + of DropTable: + discard diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create.nim new file mode 100644 index 0000000..b107344 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create.nim @@ -0,0 +1,31 @@ +import std/os +import ../../../query_builder/models/postgres/postgres_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/postgres/create_migration_table +import ../../queries/postgres/reset_table +import ../../queries/postgres/create_table + + +proc create*(rdb:PostgresConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + if isReset: + # delete table in reverse loop in tables + for i in countdown(tables.len-1, 0): + let table = tables[i] + query = createSchema(rdb, table) + query.resetMigrationTable() + query.resetTable() + + for table in tables: + table.usecaseType = Create + query = createSchema(rdb, table) + query.createTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_query_def.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_query_def.nim new file mode 100644 index 0000000..40f9e98 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_query_def.nim @@ -0,0 +1,11 @@ +import ../../../query_builder/models/postgres/postgres_types +import ../../queries/postgres/postgres_query_type +import ../../models/table +import ../../models/column + + +proc createSchema*(rdb:PostgresConnections, table:Table):PostgresSchema = + return PostgresSchema.new(rdb, table) + +proc createSchema*(rdb:PostgresConnections, table:Table, column:Column):PostgresSchema = + return PostgresSchema.new(rdb, table, column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_schema.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_schema.nim new file mode 100644 index 0000000..2c0c234 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/create_schema.nim @@ -0,0 +1,100 @@ +import std/asyncdispatch +import std/strutils +import std/strformat +import std/json +import std/tables +import std/re +import std/os +import ../../../utils/snake_to_camel +import ../../../query_builder/models/postgres/postgres_types +import ../../../query_builder/models/postgres/postgres_query +import ../../../query_builder/models/postgres/postgres_exec + + + +proc getTableInfo(rdb: PostgresConnections): Future[Table[string, seq[tuple[name: string, typ: string]]]] {.async.} = + ## get table info + var tablesInfo = initTable[string, seq[tuple[name: string, typ: string]]]() + + # get table list + let tables = await rdb.raw( + "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'" + ).get() + + for table in tables: + let tableName = table["table_name"].getStr() + + # get column info + let query = + """SELECT column_name, data_type + FROM information_schema.columns + WHERE table_name = ? + ORDER BY ordinal_position + """ + .replace(re"\s{2,}", " ") + + let columns = rdb.raw(query, %*[tableName]).get().await + + var columnInfo: seq[tuple[name: string, typ: string]] + for col in columns: + columnInfo.add(( + name: col["column_name"].getStr(), + typ: col["data_type"].getStr() + )) + + tablesInfo[tableName] = columnInfo + + return tablesInfo + + +proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: string]]]): string = + ## generate schema.nim code + var code = "import std/json" + + for tableName, columns in tablesInfo.pairs: + if tableName == "_allographer_migrations": + continue + + let tableNameCamel = tableName.snakeToCamel() + code.add("\n\n") + code.add(&"type {tableNameCamel}Table* = object\n") + code .add(&" ## {tableName}\n") + for col in columns: + let nimType = + case col.typ.toLower() + of "smallint", "integer", "bigint": + "int" + of "character", "character varying", "text", "date", "timestamp without time zone", "time without time zone", "bytea": + "string" + of "boolean": + "bool" + of "numeric": + "float" + of "jsonb": + "JsonNode" + else: + "string" # default + code.add &" {col.name}*: {nimType}\n" + + return code + + +proc createSchema*(rdb: PostgresConnections, schemaPath="") {.async.} = + ## if schemaPath is not specified, the schema will be generated in the current directory + ## + ## Default schema file name is `getCurrentDir() / "schema.nim"` + try: + let tablesInfo = rdb.getTableInfo().await + let schemaCode = generateSchemaCode(tablesInfo) + + let schemaFilePath = + if schemaPath == "": + getCurrentDir() / "schema.nim" + else: + schemaPath + + writeFile(schemaFilePath, schemaCode) + echo "schema generated successfully in ", schemaFilePath + except Exception as e: + echo "Error generating schema: ", e.msg + raise diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/drop.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/drop.nim new file mode 100644 index 0000000..4f43dbe --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/postgres/drop.nim @@ -0,0 +1,22 @@ +import std/os +import ../../../query_builder/models/postgres/postgres_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/postgres/create_migration_table +import ../../queries/postgres/drop_table + + +proc drop*(rdb:PostgresConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for table in tables: + table.usecaseType = Drop + query = createSchema(rdb, table) + query.dropTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/alter.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/alter.nim new file mode 100644 index 0000000..c15e1fd --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/alter.nim @@ -0,0 +1,49 @@ +import std/os +import ../../../query_builder/models/sqlite/sqlite_types +import ../../models/table +import ../../models/column +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/sqlite/create_migration_table +import ../../queries/sqlite/add_column +import ../../queries/sqlite/change_column +import ../../queries/sqlite/rename_column +import ../../queries/sqlite/drop_column +import ../../queries/sqlite/rename_table + + +proc alter*(rdb:SqliteConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for i, table in tables: + table.usecaseType = Alter + case table.migrationType + of CreateTable: + for column in table.columns: + column.usecaseType = Alter + case column.migrationType + of AddColumn: + query = createSchema(rdb, table, column) + query.addColumn(isReset) + of ChangeColumn: + query = createSchema(rdb, table, column) + query.changeColumn(isReset) + of RenameColumn: + query = createSchema(rdb, table, column) + query.renameColumn(isReset) + of DropColumn: + query = createSchema(rdb, table, column) + query.dropColumn(isReset) + of ChangeTable: + discard + of RenameTable: + query = createSchema(rdb, table) + query.renameTable(isReset) + of DropTable: + discard diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create.nim new file mode 100644 index 0000000..34bc633 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create.nim @@ -0,0 +1,31 @@ +import std/os +import ../../enums +import ../../../query_builder/models/sqlite/sqlite_types +import ../../models/table +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/sqlite/create_migration_table +import ../../queries/sqlite/reset_table +import ../../queries/sqlite/create_table + + +proc create*(rdb:SqliteConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + if isReset: + # delete table in reverse loop in tables + for i in countdown(tables.len-1, 0): + let table = tables[i] + query = createSchema(rdb, table) + query.resetMigrationTable() + query.resetTable() + + for table in tables: + table.usecaseType = Create + query = createSchema(rdb, table) + query.createTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_query_def.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_query_def.nim new file mode 100644 index 0000000..11c1825 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_query_def.nim @@ -0,0 +1,11 @@ +import ../../../query_builder/models/sqlite/sqlite_types +import ../../queries/sqlite/sqlite_query_type +import ../../models/table +import ../../models/column + + +proc createSchema*(rdb:SqliteConnections, table:Table):SqliteSchema = + return SqliteSchema.new(rdb, table) + +proc createSchema*(rdb:SqliteConnections, table:Table, column:Column):SqliteSchema = + return SqliteSchema.new(rdb, table, column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_schema.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_schema.nim new file mode 100644 index 0000000..c59303b --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/create_schema.nim @@ -0,0 +1,96 @@ +import std/asyncdispatch +import std/strutils +import std/strformat +import std/json +import std/tables +import std/os +import ../../../utils/snake_to_camel +import ../../../query_builder/models/sqlite/sqlite_types +import ../../../query_builder/models/sqlite/sqlite_query +import ../../../query_builder/models/sqlite/sqlite_exec + +proc getTableInfo(rdb: SqliteConnections): Future[Table[string, seq[tuple[name: string, typ: string]]]] {.async.} = + ## get table info + var tablesInfo = initTable[string, seq[tuple[name: string, typ: string]]]() + + try: + # get table list + let tables = await rdb.raw( + "SELECT name as table_name FROM sqlite_master WHERE type = 'table'" + ).get() + + for table in tables: + let tableName = table["table_name"].getStr() + + # get column info + let columns = rdb.raw("PRAGMA table_info(?)", %*[tableName]) + .get() + .await + + var columnInfo: seq[tuple[name: string, typ: string]] + for col in columns: + columnInfo.add(( + name: col["name"].getStr(), + typ: col["type"].getStr() + )) + + tablesInfo[tableName] = columnInfo + + except Exception as e: + echo "Error fetching table info: ", e.msg + raise + + return tablesInfo + +proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: string]]]): string = + ## generate schema.nim code + var code = "import std/json" + + for tableName, columns in tablesInfo.pairs: + if tableName == "_allographer_migrations": + continue + if tableName == "sqlite_sequence": + continue + + let tableNameCamel = tableName.snakeToCamel() + code.add("\n\n") + code.add(&"type {tableNameCamel}Table* = object\n") + code.add(&" ## {tableName}\n") + for col in columns: + let nimType = + case col.typ.toLower() + of "integer": + "int" + of "varchar", "text", "blob", "date", "datetime", "time": + "string" + of "tinyint": + "bool" + of "numeric", "real": + "float" + of "json": + "JsonNode" + else: + "string" # default + code.add &" {col.name}*: {nimType}\n" + + return code + +proc createSchema*(rdb: SqliteConnections, schemaPath="") {.async.} = + ## if schemaPath is not specified, the schema will be generated in the current directory + ## + ## Default schema file name is `getCurrentDir() / "schema.nim"` + try: + let tablesInfo = await rdb.getTableInfo() + let schemaCode = generateSchemaCode(tablesInfo) + + let schemaFilePath = + if schemaPath == "": + getCurrentDir() / "schema.nim" + else: + schemaPath + + writeFile(schemaFilePath, schemaCode) + echo "schema generated successfully in ", schemaFilePath + except Exception as e: + echo "Error generating schema: ", e.msg + raise diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/drop.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/drop.nim new file mode 100644 index 0000000..24b0632 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/sqlite/drop.nim @@ -0,0 +1,22 @@ +import std/os +import ../../../query_builder/models/sqlite/sqlite_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/sqlite/create_migration_table +import ../../queries/sqlite/drop_table + + +proc drop*(rdb:SqliteConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable() + + for table in tables: + table.usecaseType = Drop + query = createSchema(rdb, table) + query.dropTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/sub/migration_table_def.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/sub/migration_table_def.nim new file mode 100644 index 0000000..2d10e5a --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/sub/migration_table_def.nim @@ -0,0 +1,11 @@ +import ../../models/table +import ../../models/column + +let migrationTable* :Table = table("_allographer_migrations", [ + Column.increments("index"), + Column.string("name"), + Column.text("query"), + Column.string("checksum").index(), + Column.datetime("created_at").index(), + Column.boolean("status") +]) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/alter.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/alter.nim new file mode 100644 index 0000000..1317e19 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/alter.nim @@ -0,0 +1,49 @@ +import std/os +import ../../../query_builder/models/surreal/surreal_types +import ../../models/table +import ../../models/column +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/surreal/create_migration_table +import ../../queries/surreal/add_column +import ../../queries/surreal/change_column +import ../../queries/surreal/rename_column +import ../../queries/surreal/drop_column +import ../../queries/surreal/rename_table + + +proc alter*(rdb:SurrealConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable(isReset) + + for i, table in tables: + table.usecaseType = Alter + case table.migrationType + of CreateTable: + for column in table.columns: + column.usecaseType = Alter + case column.migrationType + of AddColumn: + query = createSchema(rdb, table, column) + query.addColumn(isReset) + of ChangeColumn: + query = createSchema(rdb, table, column) + query.changeColumn(isReset) + of RenameColumn: + query = createSchema(rdb, table, column) + query.renameColumn(isReset) + of DropColumn: + query = createSchema(rdb, table, column) + query.dropColumn(isReset) + of ChangeTable: + discard + of RenameTable: + query = createSchema(rdb, table) + query.renameTable(isReset) + of DropTable: + discard diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create.nim new file mode 100644 index 0000000..32f3bab --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create.nim @@ -0,0 +1,35 @@ +import std/os +import ../../../query_builder/models/surreal/surreal_types +import ../../queries/surreal/create_sequence_table +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/surreal/create_migration_table +import ../../queries/surreal/create_table +import ../../queries/surreal/reset_table + + +proc create*(rdb:SurrealConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + createSequenceTable(rdb) + resetSequence(rdb, Table(name:"_allographer_migrations")) + query.createMigrationTable(isReset) + + if isReset: + # delete table in reverse loop in tables + for i in countdown(tables.len-1, 0): + let table = tables[i] + resetSequence(rdb, table) + query = createSchema(rdb, table) + query.resetMigrationTable() + query.resetTable() + + for table in tables: + table.usecaseType = Create + query = createSchema(rdb, table) + query.createTable(isReset) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_query_def.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_query_def.nim new file mode 100644 index 0000000..192363e --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_query_def.nim @@ -0,0 +1,11 @@ +import ../../../query_builder/models/surreal/surreal_types +import ../../queries/surreal/surreal_query_type +import ../../models/table +import ../../models/column + + +proc createSchema*(rdb:SurrealConnections, table:Table):SurrealSchema = + return SurrealSchema.new(rdb, table) + +proc createSchema*(rdb:SurrealConnections, table:Table, column:Column):SurrealSchema = + return SurrealSchema.new(rdb, table, column) diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_schema.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_schema.nim new file mode 100644 index 0000000..2ac4ebc --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/create_schema.nim @@ -0,0 +1,102 @@ +import std/asyncdispatch +import std/strutils +import std/strformat +import std/json +import std/tables +import std/os +import ../../../utils/snake_to_camel +import ../../../query_builder/models/surreal/surreal_types +import ../../../query_builder/models/surreal/surreal_query +import ../../../query_builder/models/surreal/surreal_exec + + +proc getTableInfo(rdb: SurrealConnections): Future[Table[string, seq[tuple[name: string, typ: string]]]] {.async.} = + ## get table info from SurrealDB + var tablesInfo = initTable[string, seq[tuple[name: string, typ: string]]]() + + try: + # Get all tables using SurrealDB's INFO statement + let dbResponse = rdb.raw("INFO FOR DB").info().await + let dbInfo = dbResponse[0]["result"]["tables"].getFields() + var tables:seq[string] + for key, _ in dbInfo.pairs: + if key == "_allographer_migrations": + continue + if key == "_autoincrement_sequences": + continue + tables.add(key) + + for tableName in tables: + # Get field definitions for each table + let tableResponse = rdb.raw(&"INFO FOR TABLE {tableName}").info().await + let fields = tableResponse[0]["result"]["fields"].getFields() + + var columnInfo: seq[tuple[name: string, typ: string]] + for fieldName, fieldDef in fields.pairs: + columnInfo.add(( + name: fieldName, + typ: fieldDef.getStr().split(" ")[6] + )) + + tablesInfo[tableName] = columnInfo + + except Exception as e: + echo "Error fetching table info: ", e.msg + raise + + return tablesInfo + + +proc generateSchemaCode(tablesInfo: Table[string, seq[tuple[name: string, typ: string]]]): string = + ## generate schema.nim code + var code = "import std/json" + + for tableName, columns in tablesInfo.pairs: + if tableName == "_allographer_migrations": + continue + + let tableNameCamel = tableName.snakeToCamel() + code.add("\n\n") + code.add(&"type {tableNameCamel}Table* = object\n") + code .add(&" ## {tableName}\n") + for col in columns: + let nimType = + case col.typ.toLower() + of "int": + "int" + of "string", "datetime": + "string" + of "object": + "JsonNode" + of "bool": + "bool" + of "decimal", "float": + "float" + of "type": + "JsonNode" + else: + "string" # default + code.add &" {col.name}*: {nimType}\n" + + return code + + +proc createSchema*(rdb: SurrealConnections, schemaPath="") {.async.} = + ## if schemaPath is not specified, the schema will be generated in the current directory + ## + ## Default schema file name is `getCurrentDir() / "schema.nim"` + try: + let tablesInfo = rdb.getTableInfo().await + let schemaCode = generateSchemaCode(tablesInfo) + + let schemaFilePath = + if schemaPath == "": + getCurrentDir() / "schema.nim" + else: + schemaPath + + writeFile(schemaFilePath, schemaCode) + echo "schema generated successfully in ", schemaFilePath + except Exception as e: + echo "Error generating schema: ", e.msg + raise diff --git a/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/drop.nim b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/drop.nim new file mode 100644 index 0000000..239e477 --- /dev/null +++ b/clients/nim-allographer/src/allographer/schema_builder/usecases/surreal/drop.nim @@ -0,0 +1,22 @@ +import std/os +import ../../../query_builder/models/surreal/surreal_types +import ../../models/table +import ../../enums +import ../sub/migration_table_def +import ./create_query_def +import ../../queries/surreal/create_migration_table +import ../../queries/surreal/drop_table + + +proc drop*(rdb:SurrealConnections, tables:varargs[Table]) = + let cmd = commandLineParams() + let isReset = defined(reset) or cmd.contains("--reset") + + # create migration table + var query = createSchema(rdb, migrationTable) + query.createMigrationTable(isReset) + + for table in tables: + table.usecaseType = Drop + query = createSchema(rdb, table) + query.dropTable(isReset) diff --git a/clients/nim-allographer/src/allographer/utils.nim b/clients/nim-allographer/src/allographer/utils.nim new file mode 100644 index 0000000..7202831 --- /dev/null +++ b/clients/nim-allographer/src/allographer/utils.nim @@ -0,0 +1,13 @@ +import strutils + +func getOsName*():string = + when defined(linux) or defined(bsd): + const f = staticRead("/etc/os-release") + for row in f.split("\n"): + let kv = row.split("=") + if kv[0] == "ID": + return kv[1] + elif defined(windows): + return "windows" + else: + return "" diff --git a/clients/nim-allographer/src/allographer/utils/snake_to_camel.nim b/clients/nim-allographer/src/allographer/utils/snake_to_camel.nim new file mode 100644 index 0000000..e841b7f --- /dev/null +++ b/clients/nim-allographer/src/allographer/utils/snake_to_camel.nim @@ -0,0 +1,7 @@ +import std/strutils +import std/sequtils + + +proc snakeToCamel*(s: string): string = + ## convert snake_case to CamelCase + result = s.split("_").mapIt(it.capitalizeAscii()).join("") diff --git a/clients/nim-allographer/tests/baradb/config.nims b/clients/nim-allographer/tests/baradb/config.nims new file mode 100644 index 0000000..73d0e86 --- /dev/null +++ b/clients/nim-allographer/tests/baradb/config.nims @@ -0,0 +1,2 @@ +import os +putEnv("DB_BARADB", "true") diff --git a/clients/nim-allographer/tests/baradb/connections.nim b/clients/nim-allographer/tests/baradb/connections.nim new file mode 100644 index 0000000..b78d013 --- /dev/null +++ b/clients/nim-allographer/tests/baradb/connections.nim @@ -0,0 +1,6 @@ +import std/asyncdispatch +import std/json +import ../../src/allographer/connection +import ../../src/allographer/query_builder + +let baradb* = dbOpen(Baradb, "default", "admin", "", "127.0.0.1", 9472, maxConnections = 2, timeout = 30) diff --git a/clients/nim-allographer/tests/baradb/test_open.nim b/clients/nim-allographer/tests/baradb/test_open.nim new file mode 100644 index 0000000..596c3a1 --- /dev/null +++ b/clients/nim-allographer/tests/baradb/test_open.nim @@ -0,0 +1,13 @@ +discard """ + cmd: "nim c $file" +""" + +import std/unittest +import ../../src/allographer/connection +import ../../src/allographer/query_builder + + +suite("Baradb connection"): + test("connection"): + let rdb = dbOpen(Baradb, "default", "admin", "", "127.0.0.1", 9472) + check(rdb.isConnected()) diff --git a/clients/nim-allographer/tests/baradb/test_query.nim b/clients/nim-allographer/tests/baradb/test_query.nim new file mode 100644 index 0000000..5c89faf --- /dev/null +++ b/clients/nim-allographer/tests/baradb/test_query.nim @@ -0,0 +1,82 @@ +discard """ + cmd: "nim c -r $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import ../../src/allographer/query_builder +import ./connections + + +let rdb = baradb + +suite("Baradb query"): + test("create table raw"): + let sql = """ + CREATE TABLE IF NOT EXISTS "users" ( + "id" SERIAL PRIMARY KEY, + "name" VARCHAR(255), + "email" VARCHAR(255), + "age" INTEGER + ) + """ + waitFor rdb.raw(sql).exec() + check true + + test("insert"): + waitFor rdb.table("users").insert(%*{ "name": "Alice", "email": "alice@example.com", "age": 30 }) + waitFor rdb.table("users").insert(%*{ "name": "Bob", "email": "bob@example.com", "age": 25 }) + check true + + test("get"): + let users = waitFor rdb.select("id", "name", "email", "age").table("users").orderBy("id", Asc).get() + check users.len >= 2 + check users[0]["name"].getStr == "Alice" + check users[0]["email"].getStr == "alice@example.com" + check users[0]["age"].getInt == 30 + + test("first"): + let user = waitFor rdb.table("users").where("name", "=", "Alice").first() + check user.isSome + check user.get["name"].getStr == "Alice" + + test("find"): + let user = waitFor rdb.table("users").find(1) + check user.isSome + check user.get["id"].getInt == 1 + + test("update"): + waitFor rdb.table("users").where("name", "=", "Alice").update(%*{ "age": 31 }) + let user = waitFor rdb.table("users").where("name", "=", "Alice").first() + check user.isSome + check user.get["age"].getInt == 31 + + test("delete"): + waitFor rdb.table("users").where("name", "=", "Bob").delete() + let user = waitFor rdb.table("users").where("name", "=", "Bob").first() + check user.isNone + + test("count"): + let cnt = waitFor rdb.table("users").count() + check cnt >= 1 + + test("transaction rollback"): + waitFor rdb.begin() + waitFor rdb.table("users").insert(%*{ "name": "Charlie", "email": "charlie@example.com", "age": 40 }) + waitFor rdb.rollback() + let user = waitFor rdb.table("users").where("name", "=", "Charlie").first() + check user.isNone + + test("transaction commit"): + waitFor rdb.begin() + waitFor rdb.table("users").insert(%*{ "name": "Dave", "email": "dave@example.com", "age": 50 }) + waitFor rdb.commit() + let user = waitFor rdb.table("users").where("name", "=", "Dave").first() + check user.isSome + + test("raw select"): + let users = waitFor rdb.raw("SELECT * FROM \"users\" WHERE \"age\" > 20").get() + check users.len >= 1 diff --git a/clients/nim-allographer/tests/clear_tables.nim b/clients/nim-allographer/tests/clear_tables.nim new file mode 100644 index 0000000..463feba --- /dev/null +++ b/clients/nim-allographer/tests/clear_tables.nim @@ -0,0 +1,71 @@ +import std/asyncdispatch +import std/json +import std/strformat +import std/strutils +import ../src/allographer/query_builder + + +proc clearTables*(rdb:PostgresConnections) {.async.} = + try: + let tables = rdb.table("_allographer_migrations").orderBy("index", Desc) .get().await + for table in tables: + let tableName = table["name"].getStr() + if not tableName.startsWith("_"): + rdb.raw(&"DROP TABLE IF EXISTS \"{tableName}\"").exec().await + + rdb.raw("DROP TABLE IF EXISTS \"_allographer_migrations\"").exec().await + except: + echo "error: ", getCurrentExceptionMsg() + + +proc clearTables*(rdb:MariaDBConnections) {.async.} = + try: + let tables = rdb.table("_allographer_migrations").orderBy("index", Desc) .get().await + for table in tables: + let tableName = table["name"].getStr() + if not tableName.startsWith("_"): + rdb.raw(&"DROP TABLE IF EXISTS `{tableName}`").exec().await + + rdb.raw("DROP TABLE IF EXISTS `_allographer_migrations`").exec().await + except: + echo "error: ", getCurrentExceptionMsg() + + +proc clearTables*(rdb:MySQLConnections) {.async.} = + try: + let tables = rdb.table("_allographer_migrations").orderBy("index", Desc) .get().await + for table in tables: + let tableName = table["name"].getStr() + if not tableName.startsWith("_"): + rdb.raw(&"DROP TABLE IF EXISTS `{tableName}`").exec().await + + rdb.raw("DROP TABLE IF EXISTS `_allographer_migrations`").exec().await + except: + echo "error: ", getCurrentExceptionMsg() + + +proc clearTables*(rdb:SqliteConnections) {.async.} = + try: + let tables = rdb.table("_allographer_migrations").orderBy("index", Desc) .get().await + for table in tables: + let tableName = table["name"].getStr() + if not tableName.startsWith("_"): + rdb.raw(&"DROP TABLE IF EXISTS \"{tableName}\"").exec().await + + rdb.raw("DROP TABLE IF EXISTS \"_allographer_migrations\"").exec().await + except: + echo "error: ", getCurrentExceptionMsg() + + +proc clearTables*(rdb:SurrealConnections) {.async.} = + try: + let dbInfo = rdb.raw("INFO FOR DB").info().await + let tables = dbInfo[0]["result"]["tables"] + for (table, _) in tables.pairs: + if not table.startsWith("_"): + rdb.raw(&"REMOVE TABLE {table}").exec().await + + rdb.raw(&"REMOVE TABLE _allographer_migrations").exec().await + rdb.raw(&"REMOVE TABLE _autoincrement_sequences").exec().await + except: + echo "error: ", getCurrentExceptionMsg() diff --git a/clients/nim-allographer/tests/config.nims b/clients/nim-allographer/tests/config.nims new file mode 100644 index 0000000..66addc5 --- /dev/null +++ b/clients/nim-allographer/tests/config.nims @@ -0,0 +1,8 @@ +import os + +switch("path", "$projectDir/../src") +putEnv("DB_SQLITE", $true) +putEnv("DB_POSTGRES", $true) +putEnv("DB_MYSQL", $true) +putEnv("DB_MARIADB", $true) +putEnv("DB_SURREAL", $true) diff --git a/clients/nim-allographer/tests/mariadb/connections.nim b/clients/nim-allographer/tests/mariadb/connections.nim new file mode 100644 index 0000000..bf3b94a --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/connections.nim @@ -0,0 +1,11 @@ +import std/os +import std/strutils +import ../../src/allographer/connection + +let + mariadbUrl = getEnv("MARIA_URL") + mysqlUrl = getEnv("MY_URL") + maxConnections = getEnv("DB_MAX_CONNECTION").parseInt + timeout = getEnv("DB_TIMEOUT").parseInt + +let mariadb* = dbOpen(MariaDB, databaseUrl = asDatabaseUrl(mariadbUrl), maxConnections, timeout, shouldDisplayLog=true) diff --git a/clients/nim-allographer/tests/mariadb/test_create_schema.nim b/clients/nim-allographer/tests/mariadb/test_create_schema.nim new file mode 100644 index 0000000..4f24d93 --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/test_create_schema.nim @@ -0,0 +1,108 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -d:reset -r tests/mariadb/test_create_schema.nim + +import std/unittest +import std/asyncdispatch +import std/os +import std/strutils +import std/json +import ../../src/allographer/schema_builder +import ./connections +import ../clear_tables + + +let rdb = mariadb +let schemaFilePath = getCurrentDir() / "schema.nim" +clearTables(rdb).waitFor() + +suite "Schema output after migration": + setup: + # schema.nim ファイルの削除 + if fileExists(schemaFilePath): + removeFile(schemaFilePath) + + rdb.create( + table("int_relation", [ + Column.increments("id") + ]), + table("str_relation", [ + Column.uuid("uuid") + ]), + table("test_schema_output", [ + Column.increments("id"), + Column.integer("integer"), + Column.smallInteger("smallInteger"), + Column.mediumInteger("mediumInteger"), + Column.bigInteger("bigInteger"), + Column.decimal("decimal", 10, 3), + Column.double("double", 10, 3), + Column.float("float"), + Column.uuid("uuid"), + Column.char("char", 255), + Column.string("string"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date"), + Column.datetime("datetime"), + Column.time("time"), + Column.timestamp("timestamp"), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean"), + Column.enumField("enumField", ["A", "B", "C"]), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("int_relation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("str_relation").onDelete(SET_NULL) + ]) + ) + + + test "should generate schema.nim file": + # スキーマ生成 + rdb.createSchema(schemaFilePath).waitFor() + + # schema.nim ファイルの存在確認 + check fileExists(schemaFilePath) + + # ファイル内容の検証 + let schemaContent = readFile(schemaFilePath) + echo "schemaContent: ", schemaContent + check schemaContent.contains("type TestSchemaOutputTable* = object") + check schemaContent.contains("## test_schema_output") + check schemaContent.contains("integer*: int") + check schemaContent.contains("smallInteger*: int") + check schemaContent.contains("mediumInteger*: int") + check schemaContent.contains("bigInteger*: int") + check schemaContent.contains("decimal*: float") + check schemaContent.contains("double*: float") + check schemaContent.contains("float*: float") + check schemaContent.contains("uuid*: string") + check schemaContent.contains("char*: string") + check schemaContent.contains("string*: string") + check schemaContent.contains("text*: string") + check schemaContent.contains("mediumText*: string") + check schemaContent.contains("longText*: string") + check schemaContent.contains("date*: string") + check schemaContent.contains("datetime*: string") + check schemaContent.contains("time*: string") + check schemaContent.contains("timestamp*: string") + check schemaContent.contains("created_at*: string") + check schemaContent.contains("updated_at*: string") + check schemaContent.contains("deleted_at*: string") + check schemaContent.contains("binary*: string") + check schemaContent.contains("boolean*: bool") + check schemaContent.contains("enumField*: string") + check schemaContent.contains("json*: string") + check schemaContent.contains("int_relation_id*: int") + check schemaContent.contains("str_relation_id*: string") + + +# clearTables(rdb).waitFor() +# schema.nim ファイルの削除 +if fileExists(schemaFilePath): + removeFile(schemaFilePath) diff --git a/clients/nim-allographer/tests/mariadb/test_open.nim b/clients/nim-allographer/tests/mariadb/test_open.nim new file mode 100644 index 0000000..4e100e1 --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/test_open.nim @@ -0,0 +1,45 @@ +discard """ + cmd: "nim c $file" +""" + +# nim c -r tests/v2/mariadb/test_open.nim + +import std/unittest +import ../../src/allographer/connection +import ../../src/allographer/query_builder + + +suite("MariaDB connection"): + test("connection"): + let database = "database" + let user = "user" + let password = "pass" + let host = "mariadb" + let port = 3306 + + let rdb = dbOpen(Mariadb, database, user, password, host, port) + check(rdb.isConnected()) + + + test("connection with URL"): + let url = "mariadb://user:pass@mariadb:3306/database" + let rdb = dbOpen(Mariadb, url) + check(rdb.isConnected()) + + + test("connection with DatabaseUrl"): + let url = asDatabaseUrl("mariadb://user:pass@mariadb:3306/database") + let rdb = dbOpen(MariaDB, databaseUrl = url) + check(rdb.isConnected()) + + + test("url is not start with mariadb://"): + let url = "aaa://user:pass@mariadb:3306/database" + expect(ValueError): + discard dbOpen(Mariadb, url) + + + test("cannot reach to database"): + let url = "mariadb://john:password@host:0001/database" + expect(DbError): + discard dbOpen(Mariadb, url) diff --git a/clients/nim-allographer/tests/mariadb/test_pool_wait.nim b/clients/nim-allographer/tests/mariadb/test_pool_wait.nim new file mode 100644 index 0000000..c12fb2e --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/test_pool_wait.nim @@ -0,0 +1,29 @@ +discard """ + cmd: "nim c -d:ssl -r $file" +""" +# nim c -d:ssl r tests/mariadb/test_pool_wait.nim + +import std/[asyncdispatch, deques, os, strutils, unittest] +import ../../src/allographer/connection +import ../../src/allographer/query_builder + + +suite "MariaDB pool waiter (notify)": + test "pool size 1: two concurrent raw gets both complete": + let mariaUrl = getEnv("MARIA_URL") + let timeout = getEnv("DB_TIMEOUT").parseInt + let rdb = dbOpen(MariaDB, mariaUrl, maxConnections = 1, timeout) + + proc sel(): Future[void] {.async.} = + discard await rdb.raw("SELECT SLEEP(0.05), 1").get() + + let a = sel() + let b = sel() + waitFor all(a, b) + check rdb.pools.waiters.len == 0 + + test "pool size 0: raw get raises DbError": + let mariaUrl = getEnv("MARIA_URL") + let rdb = dbOpen(MariaDB, mariaUrl, maxConnections = 0, timeout = 0) + expect(DbError): + discard waitFor rdb.raw("SELECT 1").get() diff --git a/clients/nim-allographer/tests/mariadb/test_prepared_statement.nim b/clients/nim-allographer/tests/mariadb/test_prepared_statement.nim new file mode 100644 index 0000000..d637925 --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/test_prepared_statement.nim @@ -0,0 +1,149 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections + + +let rdb = mariadb + + +proc setup(rdb: MariadbConnections) = + rdb.create([ + table("auth", [ + Column.increments("id"), + Column.string("auth") + ]), + table("user", [ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]).waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + + +suite($rdb & " prepared statement"): + test("aged connection refresh"): + rdb.pools.maxConnectionLifetime = 1 + rdb.pools.maxConnectionIdleTime = 1 + for conn in rdb.pools.conns.mitems: + conn.createdAt = 0 + conn.lastUsedAt = 0 + + let sql = """SELECT `id`, `name`, `email`, `address` FROM `user` WHERE `id` = ?""" + let stmt = rdb.prepare(sql) + defer: + waitFor stmt.close() + + let rowOpt = stmt.first(@["1"]).waitFor + check rowOpt.isSome + + var refreshed = false + for conn in rdb.pools.conns: + if conn.createdAt > 0: + refreshed = true + break + check refreshed + + test("select"): + let stmt = rdb.prepare("""SELECT `id`, `name`, `email`, `address` FROM `user` WHERE `id` = ?""") + defer: + waitFor stmt.close() + + let args = newJArray() + args.add(newJInt(1)) + + let rows = stmt.get(args).waitFor + check rows.len == 1 + check rows[0] == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address": newJNull()} + let rowOpt = stmt.first(args).waitFor + let row = options.get(rowOpt) + check row["name"].getStr == "user1" + check stmt.getPlain(args).waitFor[0][1] == "user1" + check stmt.firstPlain(args).waitFor[1] == "user1" + + + test("logical close and clear cache"): + let sql = """SELECT `id`, `name`, `email`, `address` FROM `user` WHERE `id` = ?""" + let stmt = rdb.prepare(sql) + discard waitFor stmt.first(@["1"]) + waitFor stmt.close() + + let stmt2 = rdb.prepare(sql) + defer: + waitFor stmt2.close() + + let rowOpt = stmt2.first(@["1"]).waitFor + check rowOpt.isSome + + waitFor rdb.clearStmtCache() + let stmt3 = rdb.prepare(sql) + defer: + waitFor stmt3.close() + check stmt3.first(@["1"]).waitFor.isSome + + + test("with prepared connection context"): + let selectStmt = rdb.prepare("""SELECT `id`, `name` FROM `user` WHERE `id` = ?""") + let updateStmt = rdb.prepare("""UPDATE `user` SET `address` = ? WHERE `id` = ?""") + defer: + waitFor selectStmt.close() + waitFor updateStmt.close() + + waitFor rdb.withConn( + proc(ctx: MariadbPreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @["1"]) + await updateStmt.exec(ctx, @["ctx-address", "1"]) + ) + + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].getStr == "ctx-address" + + + test("update null"): + let stmt = rdb.prepare("""UPDATE `user` SET `address` = ? WHERE `id` = ?""") + defer: + waitFor stmt.close() + + waitFor stmt.exec(@["NULL", "1"]) + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].kind == JNull diff --git a/clients/nim-allographer/tests/mariadb/test_query.nim b/clients/nim-allographer/tests/mariadb/test_query.nim new file mode 100644 index 0000000..78f0e5c --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/test_query.nim @@ -0,0 +1,563 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +# nim c -d:reset -d:ssl -r tests/mariadb/test_query.nim + +import std/unittest +import std/asyncdispatch +import std/httpclient +import std/json +import std/options +import std/streams +import std/strformat +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = mariadb + +proc setup(rdb:MariadbConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + +suite($rdb & " get"): + test("get"): + let t = rdb.table("user").get().waitFor + let expect = %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1} + echo t[0] + echo expect + check t[0] == expect + + + test("getPlain"): + let t = rdb.table("user").getPlain().waitFor + check t[0] == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("first"): + var t = rdb.table("user").where("name", "=", "user1").first().waitFor().get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("firstPlain"): + var t = rdb.table("user").firstPlain().waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("find"): + var t = rdb.table("user").find(1).waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findString"): + var t = rdb.table("user").find("1").waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findPlain"): + var t = rdb.table("user").findPlain(1).waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("columns"): + let columns = rdb.table("user").columns().waitFor + check columns == @["id", "name", "email", "address", "submit_on", "submit_at", "auth_id"] + + + test("select"): + var t = rdb.select("name", "email").table("user").get().waitFor + check t[0] == %*{"name": "user1", "email": "user1@example.com"} + + + test("selectAs"): + var t = rdb.select("name as user_name", "email").table("user").get().waitFor + check t[0] == %*{"user_name": "user1", "email": "user1@example.com"} + + + test("selectLike"): + var t = rdb.select("email").table("user").where("email", "LIKE", "%10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "user10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "%10@example.com%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + + + test("select count"): + var t = rdb.select("COUNT(id) as count").table("user").get().waitFor + check t[0]["count"].getInt() == 10 + + + test("select max"): + var t = rdb.select("MAX(id) as max").table("user").get().waitFor + check t[0]["max"].getInt() == 10 + + + test("where"): + var t = rdb.table("user").where("auth_id", "=", "1").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("orWhere"): + var t = rdb.table("user").where("auth_id", "=", "1").orWhere("name", "=", "user2").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":2,"name":"user2","email":"user2@example.com","address":newJNull(),"submit_on":"2020-02-01","submit_at":"2020-02-01 00:00:00","auth_id":2}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("whereBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereBetween("id", [6, 9]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user7" + + + test("whereBetweenString"): + var t = rdb + .table("user") + .whereBetween("submit_at", ["2020-01-01 00:00:00", "2020-01-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereBetween("submit_on", ["2020-01-01", "2020-01-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereNotBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereNotBetween("id", [1, 4]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotBetweenString"): + var t = rdb + .table("user") + .whereNotBetween("submit_at", ["2020-2-1 00:00:00", "2020-12-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereNotBetween("submit_on", ["2020-2-1", "2020-12-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user5" + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + + test("whereNull"): + setUp(rdb) + rdb.table("user").insert(%*{"email": "user11@example.com"}).waitFor + var t = rdb + .select("id", "name", "email") + .table("user") + .whereNull("name") + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 11 + + + test("groupBy"): + var t = rdb + .table("user") + .groupBy("auth_id") + .max("id") + .waitFor() + .get() + echo t + check t == "11" + + + test("having"): + var t = rdb + .select("id", "name") + .table("user") + .groupBy("auth_id") + .groupBy("id") + .having("auth_id", "=", 1) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 1 + + + test("orderBy"): + var t = rdb.table("user") + .where("auth_id", "is not", nil) + .orderBy("auth_id", Asc) + .orderBy("id", Desc) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 9 + + + test("join"): + var t = rdb + .select("user.id", "user.name") + .table("user") + .join("auth", "auth.id", "=", "user.auth_id") + .where("auth.id", "=", "2") + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user2" + + + test("leftJoin"): + rdb.table("user").insert(%*{ + "name": "user11" + }) + .waitFor + var t = rdb + .select("user.id", "user.name", "user.auth_id") + .table("user") + .leftJoin("auth", "auth.id", "=", "user.auth_id") + .orderBy("user.id", Desc) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user11" + check t[0]["auth_id"] == newJNull() + + + test("resultIsNull"): + check rdb.table("user").find(50).waitFor().isSome == false + check newSeq[JsonNode](0) == rdb.table("user").where("id", "=", 50).get().waitFor + + +suite($rdb & " insert"): + setup: + setup(rdb) + + test("insert row"): + rdb.table("user").insert(%*{"name": "Alice"}).waitFor + let t = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insert rows"): + rdb.table("user").insert(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + + test("insertId row"): + var id = rdb.table("user").insertId(%*{"name": "Alice"}).waitFor + echo id + let t = rdb.table("user").find(id).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insertId rows"): + let ids = rdb.table("user").insertId(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + + var t = rdb.table("user").find(ids[0]).waitFor().get() + check t["name"].getStr == "Alice" + + t = rdb.table("user").find(ids[1]).waitFor().get() + check t["name"].getStr == "Bob" + + + test("insertNil"): + var id = rdb.table("user").insertId(%*{ + "name": "Alice", + "email": nil, + "address": "" + }) + .waitFor + var res = rdb.table("user").find(id).waitFor + echo res.get + check res.get["email"] == newJNull() + + res = rdb.table("user").where("email", "is", nil).first().waitFor + echo res.get + check res.get["email"] == newJNull() + + + test("insert [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").insert(user).waitFor + + let row = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check row["name"].getStr() == "Alice" + + + test("insert seq[T]"): + type User = object + name:string + + let users = @[ + User(name: "Alice"), + User(name: "Bob"), + ] + + rdb.table("user").insert(users).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + +suite($rdb & " update"): + setup: + setup(rdb) + + test("update"): + rdb.table("user").where("id", "=", 1).update(%*{"name": "Alice"}).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("update [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").where("id", "=", 1).update(user).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + +suite($rdb & " delete"): + setup(rdb) + + test("delete"): + rdb.table("user").delete(1).waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + + test("delete where"): + rdb.table("user").where("name", "=", "user1").delete().waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + +suite($rdb & " rawQuery"): + setup(rdb) + + test("get"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).get().waitFor + echo res + check res[0]["name"].getStr == "user1" + + + test("getPlain"): + let sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).getPlain().waitFor + echo res + check res[0][1] == "user1" + + + test("first"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).first().waitFor().get() + echo res + check res["name"].getStr == "user1" + + + test("firstPlain"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT name FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).firstPlain().waitFor() + echo res + check res[0] == "user1" + + + test("exec"): + var sql = "UPDATE `user` SET `name` = ? WHERE `id` = ?" + rdb.raw(sql, %*["updated user1", 1]).exec().waitFor + sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %[1]).get().waitFor + echo res + check res[0]["name"].getStr == "updated user1" + + +setup(rdb) +suite($rdb & " aggregates"): + test("count"): + var t = rdb.table("user").count().waitFor + check t == 10 + + test("max"): + var t = rdb.table("user").max("name").waitFor.get + check t == "user9" + var t2 = rdb.table("user").max("id").waitFor.get + check t2 == "10" + + test("min"): + var t = rdb.table("user").min("name").waitFor.get + check t == "user1" + var t2 = rdb.table("user").min("id").waitFor.get + check t2 == "1" + + test("avg"): + var t = rdb.table("user").avg("id").waitFor.get + check t == 5.5 + + test("sum"): + var t = rdb.table("user").sum("id").waitFor.get + check t == 55.0 + + +suite($rdb & " insert binary"): + test("should success"): + rdb.create( + table("test", [ + Column.increments("id"), + Column.binary("pic"), + ]) + ) + + let client = newAsyncHttpClient() + let response = client.getContent("https://nim-lang.org/assets/img/twitter_banner.png").waitFor() + let imageStream = newStringStream(response) + let binaryImage = imageStream.readAll() + + let id = rdb.table("test").insertId(%*{"pic": binaryImage}).waitFor() + + var res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + rdb.table("test").where("id","=", id).update(%*{"pic": binaryImage}).waitFor() + + res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/mariadb/test_schema.nim b/clients/nim-allographer/tests/mariadb/test_schema.nim new file mode 100644 index 0000000..b9ada6c --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/test_schema.nim @@ -0,0 +1,308 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = mariadb + +suite("MariaDB create table"): + test("create table"): + rdb.create( + table("IntRelation", [ + Column.increments("id") + ]), + table("StrRelation", [ + Column.uuid("uuid") + ]), + table("TypeIndex", [ + Column.increments("id"), + Column.integer("integer").unsigned().index().default(1), + Column.smallInteger("smallInteger").unsigned().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().index().default(1), + Column.bigInteger("bigInteger").unsigned().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1), + Column.double("double", 10, 3).unsigned().index().default(1.1), + Column.float("float").unsigned().index().default(1.1), + Column.uuid("uuid").index().default("A"), + Column.char("char", 255).index().default("A"), + Column.string("string").index().default("A"), + Column.text("text").index().default("A"), + Column.mediumText("mediumText").index().default("A"), + Column.longText("longText").index().default("A"), + Column.date("date").index().default(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default(), + Column.timestamp("timestamp").index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").index().default("A"), + Column.boolean("boolean").index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A"), + Column.json("json").index().default(%*{"key":"value"}), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]), + table("TypeUnique", [ + Column.increments("id"), + Column.integer("integer").unsigned().unique().index().default(1), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1), + Column.float("float").unsigned().unique().index().default(1.1), + Column.uuid("uuid").unique().index().default("A"), + Column.char("char", 255).unique().index().default("A"), + Column.string("string").unique().index().default("A"), + Column.text("text").unique().index().default("A"), + Column.mediumText("mediumText").unique().index().default("A"), + Column.longText("longText").unique().index().default("A"), + Column.date("date").unique().index().default(), + Column.datetime("datetime").unique().index().default(), + Column.time("time").unique().index().default(), + Column.timestamp("timestamp").unique().index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").unique().index().default("A"), + Column.boolean("boolean").unique().index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A"), + Column.json("json").unique().index().default(%*{"key":"value"}), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]) + ) + + + test("change columns"): + rdb.alter( + table("TypeIndex", [ + Column.integer("integer").unsigned().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().index().default(1.1).change(), + Column.float("float").unsigned().index().default(1.1).change(), + Column.uuid("uuid").index().default("A").change(), + Column.char("char", 255).index().default("A").change(), + Column.string("string").index().default("A").change(), + Column.text("text").index().default("A").change(), + Column.mediumText("mediumText").index().default("A").change(), + Column.longText("longText").index().default("A").change(), + Column.date("date").index().default().change(), + Column.datetime("datetime").index().default().change(), + Column.time("time").index().default().change(), + Column.timestamp("timestamp").index().default().change(), + Column.binary("binary").index().default("A").change(), + Column.boolean("boolean").index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").change(), + Column.json("json").index().default(%*{"key":"value"}).change(), + ]), + table("TypeUnique", [ + Column.integer("integer").unsigned().unique().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.float("float").unsigned().unique().index().default(1.1).change(), + Column.uuid("uuid").unique().index().default("A").change(), + Column.char("char", 255).unique().index().default("A").change(), + Column.string("string").unique().index().default("A").change(), + Column.text("text").unique().index().default("A").change(), + Column.mediumText("mediumText").unique().index().default("A").change(), + Column.longText("longText").unique().index().default("A").change(), + Column.date("date").unique().index().default().change(), + Column.datetime("datetime").unique().index().default().change(), + Column.time("time").unique().index().default().change(), + Column.timestamp("timestamp").unique().index().default().change(), + Column.binary("binary").unique().index().default("A").change(), + Column.boolean("boolean").unique().index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").change(), + Column.json("json").unique().index().default(%*{"key":"value"}).change(), + ]) + ) + + +suite($rdb & " alter table"): + setup: + rdb.create( + table("IntRelation", [ + Column.increments("id"), + ]), + table("StrRelation", [ + Column.uuid("uuid"), + ]), + table("TypeUnique", [ + Column.integer("num"), + Column.string("str"), + ]), + table("TypeIndex", [ + Column.integer("num"), + Column.string("str"), + ]) + ) + + + test("add columns"): + rdb.alter( + table("TypeIndex", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().index().default(1.1).add(), + Column.float("float").unsigned().index().default(1.1).add(), + Column.uuid("uuid").index().default("A").add(), + Column.char("char", 255).index().default("A").add(), + Column.string("string").index().default("A").add(), + Column.text("text").index().default("A").add(), + Column.mediumText("mediumText").index().default("A").add(), + Column.longText("longText").index().default("A").add(), + Column.date("date").index().default().add(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default().add(), + Column.timestamp("timestamp").index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").index().default("A").add(), + Column.boolean("boolean").index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").add(), + Column.json("json").index().default(%*{"key":"value"}).add(), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL).add(), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL).add() + ]), + table("TypeUnique", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().unique().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.float("float").unsigned().unique().index().default(1.1).add(), + Column.uuid("uuid").unique().index().default("A").add(), + Column.char("char", 255).unique().index().default("A").add(), + Column.string("string").unique().index().default("A").add(), + Column.text("text").unique().index().default("A").add(), + Column.mediumText("mediumText").unique().index().default("A").add(), + Column.longText("longText").unique().index().default("A").add(), + Column.date("date").unique().index().default().add(), + Column.datetime("datetime").unique().index().default().add(), + Column.time("time").unique().index().default().add(), + Column.timestamp("timestamp").unique().index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").unique().index().default("A").add(), + Column.boolean("boolean").unique().index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").add(), + Column.json("json").unique().index().default(%*{"key":"value"}).add() + ]) + ) + + + test("rename column"): + rdb.alter( + table("TypeIndex", [ + Column.renameColumn("num", "num2"), + Column.renameColumn("str", "str2"), + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("num") + check columns.contains("num2") + check not columns.contains("str") + check columns.contains("str2") + + + test("drop column"): + rdb.alter( + table("TypeIndex", [ + Column.dropColumn("str") + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("str") + + + test("rename table"): + rdb.drop( + table("TypeIndex_renamed") + ) + rdb.table("TypeIndex").insert(%*{"num":1, "str": "a"}).waitFor + + rdb.alter( + table("TypeIndex").renameTo("TypeIndex_renamed") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + res = rdb.table("TypeIndex_renamed").first().waitFor + check res.isSome + + + test("drop table"): + rdb.create( + table("TypeIndex", [ + Column.integer("num") + ]) + ) + + rdb.drop( + table("TypeIndex") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + +suite($rdb & " primary"): + test("primary key"): + rdb.create( + table("relation", [ + Column.increments("id"), + ]), + table("TypeIndex", @[ + Column.integer("index1"), + Column.integer("index2"), + Column.string("string"), + Column.foreign("relation_id").reference("id").onTable("relation").onDelete(SET_NULL) + ], + primary = @["index1", "index2"] + ) + ) + + +suite("Comment"): + test("column comment"): + rdb.create( + table("Comment", [ + Column.integer("index1").comment("index1 comment"), + Column.string("string").comment("string comment") + ]) + ) + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/mariadb/test_transaction.nim b/clients/nim-allographer/tests/mariadb/test_transaction.nim new file mode 100644 index 0000000..5bd43c8 --- /dev/null +++ b/clients/nim-allographer/tests/mariadb/test_transaction.nim @@ -0,0 +1,128 @@ +discard """ + cmd: "nim c --mm:orc -d:reset -r $file" +""" + +import std/unittest +import std/json +import std/strformat +import std/strutils +import std/options +import std/asyncdispatch +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = mariadb + +proc setUp(rdb:MariadbConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor() + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId + } + ) + + rdb.table("user").insert(users).waitFor() + + +suite("raw code transaction"): + setup: + setUp(rdb) + + test("commit"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + rdb.commit().await + except CatchableError: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor + + + test("rollback"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(CatchableError, "") + except: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor + + +suite("transaction template"): + setup: + setUp(rdb) + + test("commit"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor() + + + test("rollback"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(DbError, "") + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor() + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/mysql/connections.nim b/clients/nim-allographer/tests/mysql/connections.nim new file mode 100644 index 0000000..2ce3f7b --- /dev/null +++ b/clients/nim-allographer/tests/mysql/connections.nim @@ -0,0 +1,10 @@ +import std/os +import std/strutils +import ../../src/allographer/connection + +let + mysqlUrl = getEnv("MY_URL") + maxConnections = getEnv("DB_MAX_CONNECTION").parseInt + timeout = getEnv("DB_TIMEOUT").parseInt + +let mysql* = dbOpen(MySQL, databaseUrl = asDatabaseUrl(mysqlUrl), maxConnections, timeout, shouldDisplayLog=true) diff --git a/clients/nim-allographer/tests/mysql/test_create_schema.nim b/clients/nim-allographer/tests/mysql/test_create_schema.nim new file mode 100644 index 0000000..855a6e8 --- /dev/null +++ b/clients/nim-allographer/tests/mysql/test_create_schema.nim @@ -0,0 +1,108 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -d:reset -r tests/mysql/test_create_schema.nim + +import std/unittest +import std/asyncdispatch +import std/os +import std/strutils +import std/json +import ../../src/allographer/schema_builder +import ./connections +import ../clear_tables + + +let rdb = mysql +let schemaFilePath = getCurrentDir() / "schema.nim" +clearTables(rdb).waitFor() + +suite "Schema output after migration": + setup: + # schema.nim ファイルの削除 + if fileExists(schemaFilePath): + removeFile(schemaFilePath) + + rdb.create( + table("int_relation", [ + Column.increments("id") + ]), + table("str_relation", [ + Column.uuid("uuid") + ]), + table("test_schema_output", [ + Column.increments("id"), + Column.integer("integer"), + Column.smallInteger("smallInteger"), + Column.mediumInteger("mediumInteger"), + Column.bigInteger("bigInteger"), + Column.decimal("decimal", 10, 3), + Column.double("double", 10, 3), + Column.float("float"), + Column.uuid("uuid"), + Column.char("char", 255), + Column.string("string"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date"), + Column.datetime("datetime"), + Column.time("time"), + Column.timestamp("timestamp"), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean"), + Column.enumField("enumField", ["A", "B", "C"]), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("int_relation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("str_relation").onDelete(SET_NULL) + ]) + ) + + + test "should generate schema.nim file": + # スキーマ生成 + rdb.createSchema(schemaFilePath).waitFor() + + # schema.nim ファイルの存在確認 + check fileExists(schemaFilePath) + + # ファイル内容の検証 + let schemaContent = readFile(schemaFilePath) + echo "schemaContent: ", schemaContent + check schemaContent.contains("type TestSchemaOutputTable* = object") + check schemaContent.contains("## test_schema_output") + check schemaContent.contains("integer*: int") + check schemaContent.contains("smallInteger*: int") + check schemaContent.contains("mediumInteger*: int") + check schemaContent.contains("bigInteger*: int") + check schemaContent.contains("decimal*: float") + check schemaContent.contains("double*: float") + check schemaContent.contains("float*: float") + check schemaContent.contains("uuid*: string") + check schemaContent.contains("char*: string") + check schemaContent.contains("string*: string") + check schemaContent.contains("text*: string") + check schemaContent.contains("mediumText*: string") + check schemaContent.contains("longText*: string") + check schemaContent.contains("date*: string") + check schemaContent.contains("datetime*: string") + check schemaContent.contains("time*: string") + check schemaContent.contains("timestamp*: string") + check schemaContent.contains("created_at*: string") + check schemaContent.contains("updated_at*: string") + check schemaContent.contains("deleted_at*: string") + check schemaContent.contains("binary*: string") + check schemaContent.contains("boolean*: bool") + check schemaContent.contains("enumField*: string") + check schemaContent.contains("json*: JsonNode") + check schemaContent.contains("int_relation_id*: int") + check schemaContent.contains("str_relation_id*: string") + + +clearTables(rdb).waitFor() +# schema.nim ファイルの削除 +if fileExists(schemaFilePath): + removeFile(schemaFilePath) diff --git a/clients/nim-allographer/tests/mysql/test_open.nim b/clients/nim-allographer/tests/mysql/test_open.nim new file mode 100644 index 0000000..4bcb6c1 --- /dev/null +++ b/clients/nim-allographer/tests/mysql/test_open.nim @@ -0,0 +1,45 @@ +discard """ + cmd: "nim c $file" +""" + +# nim c -r tests/v2/mariadb/test_open.nim + +import std/unittest +import ../../src/allographer/connection +import ../../src/allographer/query_builder + + +suite("MySQL connection"): + test("connection"): + let database = "database" + let user = "user" + let password = "pass" + let host = "mariadb" + let port = 3306 + + let rdb = dbOpen(MySQL, database, user, password, host, port) + check(rdb.isConnected()) + + + test("connection with URL"): + let url = "mysql://user:pass@mariadb:3306/database" + let rdb = dbOpen(MySQL, url) + check(rdb.isConnected()) + + + test("connection with DatabaseUrl"): + let url = asDatabaseUrl("mysql://user:pass@mariadb:3306/database") + let rdb = dbOpen(MySQL, databaseUrl = url) + check(rdb.isConnected()) + + + test("url is not start with mysql://"): + let url = "aaa://user:pass@mariadb:3306/database" + expect(ValueError): + discard dbOpen(MySQL, url) + + + test("cannot reach to database"): + let url = "mysql://john:password@host:0001/database" + expect(DbError): + discard dbOpen(MySQL, url) diff --git a/clients/nim-allographer/tests/mysql/test_prepared_statement.nim b/clients/nim-allographer/tests/mysql/test_prepared_statement.nim new file mode 100644 index 0000000..029dc6d --- /dev/null +++ b/clients/nim-allographer/tests/mysql/test_prepared_statement.nim @@ -0,0 +1,149 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections + + +let rdb = mysql + + +proc setup(rdb: MysqlConnections) = + rdb.create([ + table("auth", [ + Column.increments("id"), + Column.string("auth") + ]), + table("user", [ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]).waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + + +suite($rdb & " prepared statement"): + test("aged connection refresh"): + rdb.pools.maxConnectionLifetime = 1 + rdb.pools.maxConnectionIdleTime = 1 + for conn in rdb.pools.conns.mitems: + conn.createdAt = 0 + conn.lastUsedAt = 0 + + let sql = """SELECT `id`, `name`, `email`, `address` FROM `user` WHERE `id` = ?""" + let stmt = rdb.prepare(sql) + defer: + waitFor stmt.close() + + let rowOpt = stmt.first(@["1"]).waitFor + check rowOpt.isSome + + var refreshed = false + for conn in rdb.pools.conns: + if conn.createdAt > 0: + refreshed = true + break + check refreshed + + test("select"): + let stmt = rdb.prepare("""SELECT `id`, `name`, `email`, `address` FROM `user` WHERE `id` = ?""") + defer: + waitFor stmt.close() + + let args = newJArray() + args.add(newJInt(1)) + + let rows = stmt.get(args).waitFor + check rows.len == 1 + check rows[0] == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address": newJNull()} + let rowOpt = stmt.first(args).waitFor + let row = options.get(rowOpt) + check row["name"].getStr == "user1" + check stmt.getPlain(args).waitFor[0][1] == "user1" + check stmt.firstPlain(args).waitFor[1] == "user1" + + + test("logical close and clear cache"): + let sql = """SELECT `id`, `name`, `email`, `address` FROM `user` WHERE `id` = ?""" + let stmt = rdb.prepare(sql) + discard waitFor stmt.first(@["1"]) + waitFor stmt.close() + + let stmt2 = rdb.prepare(sql) + defer: + waitFor stmt2.close() + + let rowOpt = stmt2.first(@["1"]).waitFor + check rowOpt.isSome + + waitFor rdb.clearStmtCache() + let stmt3 = rdb.prepare(sql) + defer: + waitFor stmt3.close() + check stmt3.first(@["1"]).waitFor.isSome + + + test("with prepared connection context"): + let selectStmt = rdb.prepare("""SELECT `id`, `name` FROM `user` WHERE `id` = ?""") + let updateStmt = rdb.prepare("""UPDATE `user` SET `address` = ? WHERE `id` = ?""") + defer: + waitFor selectStmt.close() + waitFor updateStmt.close() + + waitFor rdb.withConn( + proc(ctx: MysqlPreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @["1"]) + await updateStmt.exec(ctx, @["ctx-address", "1"]) + ) + + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].getStr == "ctx-address" + + + test("update null"): + let stmt = rdb.prepare("""UPDATE `user` SET `address` = ? WHERE `id` = ?""") + defer: + waitFor stmt.close() + + waitFor stmt.exec(@["NULL", "1"]) + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].kind == JNull diff --git a/clients/nim-allographer/tests/mysql/test_query.nim b/clients/nim-allographer/tests/mysql/test_query.nim new file mode 100644 index 0000000..2781375 --- /dev/null +++ b/clients/nim-allographer/tests/mysql/test_query.nim @@ -0,0 +1,565 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +# nim c -d:reset -d:ssl -r tests/mysql/test_query.nim + +import std/unittest +import std/asyncdispatch +import std/httpclient +import std/json +import std/options +import std/streams +import std/strformat +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = mysql + +proc setup(rdb:MysqlConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + +suite($rdb & " get"): + test("get"): + let t = rdb.table("user").get().waitFor + let expect = %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1} + echo t[0] + echo expect + check t[0] == expect + + + test("getPlain"): + let t = rdb.table("user").getPlain().waitFor + check t[0] == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("first"): + var t = rdb.table("user").where("name", "=", "user1").first().waitFor().get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("firstPlain"): + var t = rdb.table("user").firstPlain().waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("find"): + var t = rdb.table("user").find(1).waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findString"): + var t = rdb.table("user").find("1").waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findPlain"): + var t = rdb.table("user").findPlain(1).waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("columns"): + let columns = rdb.table("user").columns().waitFor + check columns == @["id", "name", "email", "address", "submit_on", "submit_at", "auth_id"] + + + test("select"): + var t = rdb.select("name", "email").table("user").get().waitFor + check t[0] == %*{"name": "user1", "email": "user1@example.com"} + + + test("selectAs"): + var t = rdb.select("name as user_name", "email").table("user").get().waitFor + check t[0] == %*{"user_name": "user1", "email": "user1@example.com"} + + + test("selectLike"): + var t = rdb.select("email").table("user").where("email", "LIKE", "%10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "user10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "%10@example.com%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + + + test("select count"): + var t = rdb.select("COUNT(id) as count").table("user").get().waitFor + check t[0]["count"].getInt() == 10 + + + test("select max"): + var t = rdb.select("MAX(id) as max").table("user").get().waitFor + check t[0]["max"].getInt() == 10 + + + test("where"): + var t = rdb.table("user").where("auth_id", "=", "1").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("orWhere"): + var t = rdb.table("user").where("auth_id", "=", "1").orWhere("name", "=", "user2").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":2,"name":"user2","email":"user2@example.com","address":newJNull(),"submit_on":"2020-02-01","submit_at":"2020-02-01 00:00:00","auth_id":2}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("whereBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereBetween("id", [6, 9]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user7" + + + test("whereBetweenString"): + var t = rdb + .table("user") + .whereBetween("submit_at", ["2020-01-01 00:00:00", "2020-01-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereBetween("submit_on", ["2020-01-01", "2020-01-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereNotBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereNotBetween("id", [1, 4]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotBetweenString"): + var t = rdb + .table("user") + .whereNotBetween("submit_at", ["2020-2-1 00:00:00", "2020-12-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereNotBetween("submit_on", ["2020-2-1", "2020-12-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user5" + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + + test("whereNull"): + setUp(rdb) + rdb.table("user").insert(%*{"email": "user11@example.com"}).waitFor + var t = rdb + .select("id", "name", "email") + .table("user") + .whereNull("name") + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 11 + + + test("groupBy"): + var t = rdb + .table("user") + .where("auth_id", "=", 1) + .groupBy("auth_id") + .max("id") + .waitFor() + .get() + echo t + check t == "9" + + + test("having"): + var t = rdb + .select("id", "name") + .table("user") + .groupBy("auth_id") + .groupBy("id") + .having("auth_id", "=", 1) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 1 + + + test("orderBy"): + var t = rdb.table("user") + .where("auth_id", "is not", nil) + .orderBy("auth_id", Asc) + .orderBy("id", Desc) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 9 + + + test("join"): + var t = rdb + .select("user.id", "user.name") + .table("user") + .join("auth", "auth.id", "=", "user.auth_id") + .where("auth.id", "=", "2") + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user2" + + + test("leftJoin"): + rdb.table("user").insert(%*{ + "name": "user11" + }) + .waitFor + var t = rdb + .select("user.id", "user.name", "user.auth_id") + .table("user") + .leftJoin("auth", "auth.id", "=", "user.auth_id") + .orderBy("user.id", Desc) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user11" + check t[0]["auth_id"] == newJNull() + + + test("resultIsNull"): + check rdb.table("user").find(50).waitFor().isSome == false + check newSeq[JsonNode](0) == rdb.table("user").where("id", "=", 50).get().waitFor + + +suite($rdb & " insert"): + setup: + setup(rdb) + + test("insert row"): + rdb.table("user").insert(%*{"name": "Alice"}).waitFor + let t = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insert rows"): + rdb.table("user").insert(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + + test("insertId row"): + var id = rdb.table("user").insertId(%*{"name": "Alice"}).waitFor + echo id + let t = rdb.table("user").find(id).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insertId rows"): + let ids = rdb.table("user").insertId(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + + var t = rdb.table("user").find(ids[0]).waitFor().get() + check t["name"].getStr == "Alice" + + t = rdb.table("user").find(ids[1]).waitFor().get() + check t["name"].getStr == "Bob" + + + test("insertNil"): + var id = rdb.table("user").insertId(%*{ + "name": "Alice", + "email": nil, + "address": "" + }) + .waitFor + var res = rdb.table("user").find(id).waitFor + echo res.get + check res.get["email"] == newJNull() + + res = rdb.table("user").where("email", "is", nil).first().waitFor + echo res.get + check res.get["email"] == newJNull() + + + + test("insert [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").insert(user).waitFor + + let row = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check row["name"].getStr() == "Alice" + + + test("insert seq[T]"): + type User = object + name:string + + let users = @[ + User(name: "Alice"), + User(name: "Bob"), + ] + + rdb.table("user").insert(users).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + +suite($rdb & " update"): + setup: + setup(rdb) + + test("update"): + rdb.table("user").where("id", "=", 1).update(%*{"name": "Alice"}).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("update [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").where("id", "=", 1).update(user).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + +suite($rdb & " delete"): + setup(rdb) + + test("delete"): + rdb.table("user").delete(1).waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + + test("delete where"): + rdb.table("user").where("name", "=", "user1").delete().waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + +suite($rdb & " rawQuery"): + setup(rdb) + + test("get"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).get().waitFor + echo res + check res[0]["name"].getStr == "user1" + + + test("getPlain"): + let sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).getPlain().waitFor + echo res + check res[0][1] == "user1" + + + test("first"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).first().waitFor().get() + echo res + check res["name"].getStr == "user1" + + + test("firstPlain"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT name FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %*[1]).firstPlain().waitFor() + echo res + check res[0] == "user1" + + + test("exec"): + var sql = "UPDATE `user` SET `name` = ? WHERE `id` = ?" + rdb.raw(sql, %*["updated user1", 1]).exec().waitFor + sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %[1]).get().waitFor + echo res + check res[0]["name"].getStr == "updated user1" + + +setup(rdb) +suite($rdb & " aggregates"): + test("count"): + var t = rdb.table("user").count().waitFor + check t == 10 + + test("max"): + var t = rdb.table("user").max("name").waitFor.get + check t == "user9" + var t2 = rdb.table("user").max("id").waitFor.get + check t2 == "10" + + test("min"): + var t = rdb.table("user").min("name").waitFor.get + check t == "user1" + var t2 = rdb.table("user").min("id").waitFor.get + check t2 == "1" + + test("avg"): + var t = rdb.table("user").avg("id").waitFor.get + check t == 5.5 + + test("sum"): + var t = rdb.table("user").sum("id").waitFor.get + check t == 55.0 + + +suite($rdb & " insert binary"): + test("should success"): + rdb.create( + table("test", [ + Column.increments("id"), + Column.binary("pic"), + ]) + ) + + let client = newAsyncHttpClient() + let response = client.getContent("https://nim-lang.org/assets/img/twitter_banner.png").waitFor() + let imageStream = newStringStream(response) + let binaryImage = imageStream.readAll() + + let id = rdb.table("test").insertId(%*{"pic": binaryImage}).waitFor() + + var res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + rdb.table("test").where("id","=", id).update(%*{"pic": binaryImage}).waitFor() + + res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/mysql/test_schema.nim b/clients/nim-allographer/tests/mysql/test_schema.nim new file mode 100644 index 0000000..fe9c282 --- /dev/null +++ b/clients/nim-allographer/tests/mysql/test_schema.nim @@ -0,0 +1,617 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = mysql + +suite("MySQL create table"): + test("create table"): + rdb.create( + table("IntRelation", [ + Column.increments("id") + ]), + table("StrRelation", [ + Column.uuid("uuid") + ]), + # text, binary, json column can't use default value and index + table("TypeIndex", [ + Column.increments("id"), + Column.integer("integer").unsigned().index().default(1), + Column.smallInteger("smallInteger").unsigned().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().index().default(1), + Column.bigInteger("bigInteger").unsigned().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1), + Column.double("double", 10, 3).unsigned().index().default(1.1), + Column.float("float").unsigned().index().default(1.1), + Column.uuid("uuid").index().default("A"), + Column.char("char", 255).index().default("A"), + Column.string("string").index().default("A"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date").index().default(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default(), + Column.timestamp("timestamp").index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean").index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A"), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]), + # text, binary, json column can't use default value, index and unique + table("TypeUnique", [ + Column.increments("id"), + Column.integer("integer").unsigned().unique().index().default(1), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1), + Column.float("float").unsigned().unique().index().default(1.1), + Column.uuid("uuid").unique().index().default("A"), + Column.char("char", 255).unique().index().default("A"), + Column.string("string").unique().index().default("A"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date").unique().index().default(), + Column.datetime("datetime").unique().index().default(), + Column.time("time").unique().index().default(), + Column.timestamp("timestamp").unique().index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean").unique().index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A"), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]) + ) + + + test("change columns"): + rdb.alter( + table("TypeIndex", [ + Column.integer("integer").unsigned().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().index().default(1.1).change(), + Column.float("float").unsigned().index().default(1.1).change(), + Column.uuid("uuid").index().default("A").change(), + Column.char("char", 255).index().default("A").change(), + Column.string("string").index().default("A").change(), + Column.text("text").change(), + Column.mediumText("mediumText").change(), + Column.longText("longText").change(), + Column.date("date").index().default().change(), + Column.datetime("datetime").index().default().change(), + Column.time("time").index().default().change(), + Column.timestamp("timestamp").index().default().change(), + Column.binary("binary").change(), + Column.boolean("boolean").index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").change(), + Column.json("json").change(), + ]), + table("TypeUnique", [ + Column.integer("integer").unsigned().unique().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.float("float").unsigned().unique().index().default(1.1).change(), + Column.uuid("uuid").unique().index().default("A").change(), + Column.char("char", 255).unique().index().default("A").change(), + Column.string("string").unique().index().default("A").change(), + Column.text("text").change(), + Column.mediumText("mediumText").change(), + Column.longText("longText").change(), + Column.date("date").unique().index().default().change(), + Column.datetime("datetime").unique().index().default().change(), + Column.time("time").unique().index().default().change(), + Column.timestamp("timestamp").unique().index().default().change(), + Column.binary("binary").change(), + Column.boolean("boolean").unique().index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").change(), + Column.json("json").change(), + ]) + ) + + +suite("error in text, blob, json column for default, index and unique"): + suite("create table"): + test("text"): + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.text("text").default("A"), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.text("text").index(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.text("text").unique(), + ]) + ) + + test("mediumText"): + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.mediumText("mediumText").default("A"), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.mediumText("mediumText").index(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.mediumText("mediumText").unique(), + ]) + ) + + test("longText"): + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.longText("longText").default("A"), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.longText("longText").index(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.longText("longText").unique(), + ]) + ) + + test("binary"): + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.binary("binary").default("A"), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.binary("binary").index(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.binary("binary").unique(), + ]) + ) + + test("json"): + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.json("json").default(%*{"key": "value"}), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.json("json").index(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.json("json").unique(), + ]) + ) + + + suite("add column"): + test("text"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.text("text").default("A").add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.text("text").index().add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.text("text").unique().add(), + ]) + ) + + test("mediumText"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.mediumText("mediumText").default("A").add(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.mediumText("mediumText").index().add(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.mediumText("mediumText").unique().add(), + ]) + ) + + test("longText"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.longText("longText").default("A").add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.longText("longText").index().add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.longText("longText").unique().add(), + ]) + ) + + test("binary"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.binary("binary").default("A").add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.binary("binary").index().add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.binary("binary").unique().add(), + ]) + ) + + test("json"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.json("json").default(%*{"key": "value"}).add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.json("json").index().add(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.json("json").unique().add(), + ]) + ) + + + suite("change column"): + test("text"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.text("text").default("A").change(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.text("text").index().change(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.text("text").unique().change(), + ]) + ) + + test("mediumText"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.mediumText("mediumText").default("A").change(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.mediumText("mediumText").index().change(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.mediumText("mediumText").unique().change(), + ]) + ) + + test("longText"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.longText("longText").default("A").change(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.longText("longText").index().change(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.longText("longText").unique().change(), + ]) + ) + + test("binary"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.binary("binary").default("A").change(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.binary("binary").index().change(), + ]) + ) + expect DbError: + rdb.create( + table("TypeIndex", [ + Column.binary("binary").unique().change(), + ]) + ) + + test("json"): + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.json("json").default(%*{"key": "value"}).change(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.json("json").index().change(), + ]) + ) + expect DbError: + rdb.alter( + table("TypeIndex", [ + Column.json("json").unique().change(), + ]) + ) + + +suite("MySQL alter table"): + setup: + rdb.create( + table("IntRelation", [ + Column.increments("id"), + ]), + table("StrRelation", [ + Column.uuid("uuid"), + ]), + table("TypeUnique", [ + Column.integer("num"), + Column.string("str"), + ]), + table("TypeIndex", [ + Column.integer("num"), + Column.string("str"), + ]) + ) + + + test("add columns"): + rdb.alter( + table("TypeIndex", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().index().default(1.1).add(), + Column.float("float").unsigned().index().default(1.1).add(), + Column.uuid("uuid").index().default("A").add(), + Column.char("char", 255).index().default("A").add(), + Column.string("string").index().default("A").add(), + Column.text("text").add(), + Column.mediumText("mediumText").add(), + Column.longText("longText").add(), + Column.date("date").index().default().add(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default().add(), + Column.timestamp("timestamp").index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").add(), + Column.boolean("boolean").index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").add(), + Column.json("json").add(), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL).add(), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL).add() + ]), + table("TypeUnique", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().unique().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.float("float").unsigned().unique().index().default(1.1).add(), + Column.uuid("uuid").unique().index().default("A").add(), + Column.char("char", 255).unique().index().default("A").add(), + Column.string("string").unique().index().default("A").add(), + Column.text("text").add(), + Column.mediumText("mediumText").add(), + Column.longText("longText").add(), + Column.date("date").unique().index().default().add(), + Column.datetime("datetime").unique().index().default().add(), + Column.time("time").unique().index().default().add(), + Column.timestamp("timestamp").unique().index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").add(), + Column.boolean("boolean").unique().index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").add(), + Column.json("json").add() + ]) + ) + + + test("rename column"): + rdb.alter( + table("TypeIndex", [ + Column.renameColumn("num", "num2"), + Column.renameColumn("str", "str2"), + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("num") + check columns.contains("num2") + check not columns.contains("str") + check columns.contains("str2") + + + test("drop column"): + rdb.alter( + table("TypeIndex", [ + Column.dropColumn("str") + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("str") + + + test("rename table"): + rdb.drop( + table("TypeIndex_renamed") + ) + rdb.table("TypeIndex").insert(%*{"num":1, "str": "a"}).waitFor + + rdb.alter( + table("TypeIndex").renameTo("TypeIndex_renamed") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + res = rdb.table("TypeIndex_renamed").first().waitFor + check res.isSome + + + test("drop table"): + rdb.create( + table("TypeIndex", [ + Column.integer("num") + ]) + ) + + rdb.drop( + table("TypeIndex") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + +suite($rdb & " primary"): + test("primary key"): + rdb.create( + table("relation", [ + Column.increments("id"), + ]), + table("TypeIndex", @[ + Column.integer("index1"), + Column.integer("index2"), + Column.string("string"), + Column.foreign("relation_id").reference("id").onTable("relation").onDelete(SET_NULL) + ], + primary = @["index1", "index2"] + ) + ) + + +suite("Comment"): + test("column comment"): + rdb.create( + table("Comment", [ + Column.integer("index1").comment("index1 comment"), + Column.string("string").comment("string comment") + ]) + ) + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/mysql/test_transaction.nim b/clients/nim-allographer/tests/mysql/test_transaction.nim new file mode 100644 index 0000000..ba5369c --- /dev/null +++ b/clients/nim-allographer/tests/mysql/test_transaction.nim @@ -0,0 +1,128 @@ +discard """ + cmd: "nim c --mm:orc -d:reset -r $file" +""" + +import std/unittest +import std/json +import std/strformat +import std/strutils +import std/options +import std/asyncdispatch +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = mysql + +proc setUp(rdb:MysqlConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor() + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId + } + ) + + rdb.table("user").insert(users).waitFor() + + +suite("raw code transaction"): + setup: + setUp(rdb) + + test("commit"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + rdb.commit().await + except CatchableError: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor + + + test("rollback"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(CatchableError, "") + except: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor + + +suite("transaction template"): + setup: + setUp(rdb) + + test("commit"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor() + + + test("rollback"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(DbError, "") + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor() + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/postgres/connections.nim b/clients/nim-allographer/tests/postgres/connections.nim new file mode 100644 index 0000000..e61ab65 --- /dev/null +++ b/clients/nim-allographer/tests/postgres/connections.nim @@ -0,0 +1,10 @@ +import std/os +import std/strutils +import ../../src/allographer/connection + +let + pgUrl = getEnv("PG_URL") + maxConnections = getEnv("DB_MAX_CONNECTION").parseInt + timeout = getEnv("DB_TIMEOUT").parseInt + +let postgres* = dbOpen(PostgreSQL, databaseUrl = asDatabaseUrl(pgUrl), maxConnections, timeout, shouldDisplayLog=true) diff --git a/clients/nim-allographer/tests/postgres/test_create_schema.nim b/clients/nim-allographer/tests/postgres/test_create_schema.nim new file mode 100644 index 0000000..b064d67 --- /dev/null +++ b/clients/nim-allographer/tests/postgres/test_create_schema.nim @@ -0,0 +1,107 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -d:reset -r tests/postgres/test_create_schema.nim + +import std/unittest +import std/asyncdispatch +import std/os +import std/strutils +import std/json +import ../../src/allographer/schema_builder +import ./connections +import ../clear_tables + + +let rdb = postgres +let schemaFilePath = getCurrentDir() / "schema.nim" + +suite "Schema output after migration": + setup: + # schema.nim ファイルの削除 + if fileExists(schemaFilePath): + removeFile(schemaFilePath) + + rdb.create( + table("int_relation", [ + Column.increments("id") + ]), + table("str_relation", [ + Column.uuid("uuid") + ]), + table("test_schema_output", [ + Column.increments("id"), + Column.integer("integer"), + Column.smallInteger("smallInteger"), + Column.mediumInteger("mediumInteger"), + Column.bigInteger("bigInteger"), + Column.decimal("decimal", 10, 3), + Column.double("double", 10, 3), + Column.float("float"), + Column.uuid("uuid"), + Column.char("char", 255), + Column.string("string"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date"), + Column.datetime("datetime"), + Column.time("time"), + Column.timestamp("timestamp"), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean"), + Column.enumField("enumField", ["A", "B", "C"]), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("int_relation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("str_relation").onDelete(SET_NULL) + ]) + ) + + + test "should generate schema.nim file": + # スキーマ生成 + rdb.createSchema(schemaFilePath).waitFor() + + # schema.nim ファイルの存在確認 + check fileExists(schemaFilePath) + + # ファイル内容の検証 + let schemaContent = readFile(schemaFilePath) + echo "schemaContent: ", schemaContent + check schemaContent.contains("type TestSchemaOutputTable* = object") + check schemaContent.contains("## test_schema_output") + check schemaContent.contains("integer*: int") + check schemaContent.contains("smallInteger*: int") + check schemaContent.contains("mediumInteger*: int") + check schemaContent.contains("bigInteger*: int") + check schemaContent.contains("decimal*: float") + check schemaContent.contains("double*: float") + check schemaContent.contains("float*: float") + check schemaContent.contains("uuid*: string") + check schemaContent.contains("char*: string") + check schemaContent.contains("string*: string") + check schemaContent.contains("text*: string") + check schemaContent.contains("mediumText*: string") + check schemaContent.contains("longText*: string") + check schemaContent.contains("date*: string") + check schemaContent.contains("datetime*: string") + check schemaContent.contains("time*: string") + check schemaContent.contains("timestamp*: string") + check schemaContent.contains("created_at*: string") + check schemaContent.contains("updated_at*: string") + check schemaContent.contains("deleted_at*: string") + check schemaContent.contains("binary*: string") + check schemaContent.contains("boolean*: bool") + check schemaContent.contains("enumField*: string") + check schemaContent.contains("json*: JsonNode") + check schemaContent.contains("int_relation_id*: int") + check schemaContent.contains("str_relation_id*: string") + + +clearTables(rdb).waitFor() +# schema.nim ファイルの削除 +if fileExists(schemaFilePath): + removeFile(schemaFilePath) diff --git a/clients/nim-allographer/tests/postgres/test_open.nim b/clients/nim-allographer/tests/postgres/test_open.nim new file mode 100644 index 0000000..2971997 --- /dev/null +++ b/clients/nim-allographer/tests/postgres/test_open.nim @@ -0,0 +1,45 @@ +discard """ + cmd: "nim c $file" +""" + +# nim c -r tests/v2/postgres/test_open.nim + +import std/unittest +import ../../src/allographer/connection +import ../../src/allographer/query_builder + + +suite("PostgreSQL connection"): + test("connection"): + let database = "database" + let user = "user" + let password = "pass" + let host = "postgres" + let port = 5432 + + let rdb = dbOpen(PostgreSQL, database, user, password, host, port) + check(rdb.isConnected()) + + + test("connection with URL"): + let url = "postgresql://user:pass@postgres:5432/database" + let rdb = dbOpen(PostgreSQL, url) + check(rdb.isConnected()) + + + test("connection with DatabaseUrl"): + let url = asDatabaseUrl("postgresql://user:pass@postgres:5432/database") + let rdb = dbOpen(PostgreSQL, databaseUrl = url) + check(rdb.isConnected()) + + + test("url is not start with postgresql://"): + let url = "mysql://user:pass@postgres:5432/database" + expect(ValueError): + discard dbOpen(PostgreSQL, url) + + + test("cannot reach to database"): + let url = "postgresql://john:password@host:0001/database" + expect(DbError): + discard dbOpen(PostgreSQL, url) diff --git a/clients/nim-allographer/tests/postgres/test_pool_wait.nim b/clients/nim-allographer/tests/postgres/test_pool_wait.nim new file mode 100644 index 0000000..220e862 --- /dev/null +++ b/clients/nim-allographer/tests/postgres/test_pool_wait.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +import std/[asyncdispatch, os, strutils, unittest] +import ../../src/allographer/connection +import ../../src/allographer/query_builder + + +suite "PostgreSQL pool waiter (notify)": + test "pool size 1: two concurrent raw gets both complete": + let pgUrl = getEnv("PG_URL") + let timeout = getEnv("DB_TIMEOUT").parseInt + let rdb = dbOpen(PostgreSQL, pgUrl, maxConnections = 1, timeout) + proc sel(): Future[void] {.async.} = + discard await rdb.raw("SELECT 1").get() + let a = sel() + let b = sel() + waitFor all(a, b) + + test "pool size 0: raw get raises DbError": + let pgUrl = getEnv("PG_URL") + let rdb = dbOpen(PostgreSQL, pgUrl, maxConnections = 0, timeout = 0) + expect(DbError): + discard waitFor rdb.raw("SELECT 1").get() diff --git a/clients/nim-allographer/tests/postgres/test_prepared_statement.nim b/clients/nim-allographer/tests/postgres/test_prepared_statement.nim new file mode 100644 index 0000000..fb79b4b --- /dev/null +++ b/clients/nim-allographer/tests/postgres/test_prepared_statement.nim @@ -0,0 +1,149 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections + + +let rdb = postgres + + +proc setup(rdb: PostgresConnections) = + rdb.create([ + table("auth", [ + Column.increments("id"), + Column.string("auth") + ]), + table("user", [ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]).waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + + +suite($rdb & " prepared statement"): + test("aged connection refresh"): + rdb.pools.maxConnectionLifetime = 1 + rdb.pools.maxConnectionIdleTime = 1 + for conn in rdb.pools.conns.mitems: + conn.createdAt = 0 + conn.lastUsedAt = 0 + + let sql = """SELECT "id", "name", "email", "address" FROM "user" WHERE "id" = ?""" + let stmt = rdb.prepare(sql) + defer: + waitFor stmt.close() + + let rowOpt = stmt.first(@["1"]).waitFor + check rowOpt.isSome + + var refreshed = false + for conn in rdb.pools.conns: + if conn.createdAt > 0: + refreshed = true + break + check refreshed + + test("select"): + let stmt = rdb.prepare("""SELECT "id", "name", "email", "address" FROM "user" WHERE "id" = ?""") + defer: + waitFor stmt.close() + + let args = newJArray() + args.add(newJInt(1)) + + let rows = stmt.get(args).waitFor + check rows.len == 1 + check rows[0] == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address": newJNull()} + let rowOpt = stmt.first(args).waitFor + let row = options.get(rowOpt) + check row["name"].getStr == "user1" + check stmt.getPlain(args).waitFor[0][1] == "user1" + check stmt.firstPlain(args).waitFor[1] == "user1" + + + test("logical close and clear cache"): + let sql = """SELECT "id", "name", "email", "address" FROM "user" WHERE "id" = ?""" + let stmt = rdb.prepare(sql) + discard waitFor stmt.first(@["1"]) + waitFor stmt.close() + + let stmt2 = rdb.prepare(sql) + defer: + waitFor stmt2.close() + + let rowOpt = stmt2.first(@["1"]).waitFor + check rowOpt.isSome + + waitFor rdb.clearStmtCache() + let stmt3 = rdb.prepare(sql) + defer: + waitFor stmt3.close() + check stmt3.first(@["1"]).waitFor.isSome + + + test("with prepared connection context"): + let selectStmt = rdb.prepare("""SELECT "id", "name" FROM "user" WHERE "id" = ?""") + let updateStmt = rdb.prepare("""UPDATE "user" SET "address" = ? WHERE "id" = ?""") + defer: + waitFor selectStmt.close() + waitFor updateStmt.close() + + waitFor rdb.withConn( + proc(ctx: PostgresPreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @["1"]) + await updateStmt.exec(ctx, @["ctx-address", "1"]) + ) + + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].getStr == "ctx-address" + + + test("update null"): + let stmt = rdb.prepare("""UPDATE "user" SET "address" = ? WHERE "id" = ?""") + defer: + waitFor stmt.close() + + waitFor stmt.exec(@["NULL", "1"]) + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].kind == JNull diff --git a/clients/nim-allographer/tests/postgres/test_query.nim b/clients/nim-allographer/tests/postgres/test_query.nim new file mode 100644 index 0000000..40f812e --- /dev/null +++ b/clients/nim-allographer/tests/postgres/test_query.nim @@ -0,0 +1,561 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +# nim c -d:reset -d:ssl -r tests/postgres/test_query.nim + +import std/unittest +import std/asyncdispatch +import std/httpclient +import std/json +import std/options +import std/streams +import std/strformat +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = postgres + +# ============================================================================= +# test +# ============================================================================= + +proc setup(rdb:PostgresConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + +suite($rdb & " get"): + test("get"): + let t = rdb.table("user").get().waitFor + let expect = %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1} + echo t[0] + echo expect + check t[0] == expect + + + test("getPlain"): + let t = rdb.table("user").getPlain().waitFor + check t[0] == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("first"): + var t = rdb.table("user").where("name", "=", "user1").first().waitFor().get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("firstPlain"): + var t = rdb.table("user").firstPlain().waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("find"): + var t = rdb.table("user").find(1).waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findString"): + var t = rdb.table("user").find("1").waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findPlain"): + var t = rdb.table("user").findPlain(1).waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("columns"): + let columns = rdb.table("user").columns().waitFor + check columns == @["id", "name", "email", "address", "submit_on", "submit_at", "auth_id"] + + + test("select"): + var t = rdb.select("name", "email").table("user").get().waitFor + check t[0] == %*{"name": "user1", "email": "user1@example.com"} + + + test("selectAs"): + var t = rdb.select("name as user_name", "email").table("user").get().waitFor + check t[0] == %*{"user_name": "user1", "email": "user1@example.com"} + + + test("selectLike"): + var t = rdb.select("email").table("user").where("email", "LIKE", "%10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "user10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "%10@example.com%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + + + test("select count"): + var t = rdb.select("COUNT(id) as count").table("user").get().waitFor + check t[0]["count"].getInt() == 10 + + + test("where"): + var t = rdb.table("user").where("auth_id", "=", "1").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("orWhere"): + var t = rdb.table("user").where("auth_id", "=", "1").orWhere("name", "=", "user2").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":2,"name":"user2","email":"user2@example.com","address":newJNull(),"submit_on":"2020-02-01","submit_at":"2020-02-01 00:00:00","auth_id":2}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("whereBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereBetween("id", [6, 9]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user7" + + + test("whereBetweenString"): + var t = rdb + .table("user") + .whereBetween("submit_at", ["2020-01-01 00:00:00", "2020-01-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereBetween("submit_on", ["2020-01-01", "2020-01-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereNotBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereNotBetween("id", [1, 4]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotBetweenString"): + var t = rdb + .table("user") + .whereNotBetween("submit_at", ["2020-2-1 00:00:00", "2020-12-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereNotBetween("submit_on", ["2020-2-1", "2020-12-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user5" + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + + test("whereNull"): + setUp(rdb) + rdb.table("user").insert(%*{"email": "user11@example.com"}).waitFor + var t = rdb + .select("id", "name", "email") + .table("user") + .whereNull("name") + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 11 + + + test("groupBy"): + var t = rdb + .table("user") + .groupBy("auth_id") + .max("id") + .waitFor() + .get() + echo t + check t == "11" + + + test("having"): + var t = rdb + .select("id", "name") + .table("user") + .groupBy("auth_id") + .groupBy("id") + .having("auth_id", "=", 1) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 1 + + + test("orderBy"): + var t = rdb.table("user") + .where("auth_id", "is not", nil) + .orderBy("auth_id", Asc) + .orderBy("id", Desc) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 9 + + + test("join"): + var t = rdb + .select("user.id", "user.name") + .table("user") + .join("auth", "auth.id", "=", "user.auth_id") + .where("auth.id", "=", "2") + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user2" + + + test("leftJoin"): + rdb.table("user").insert(%*{ + "name": "user11" + }) + .waitFor + var t = rdb + .select("user.id", "user.name", "user.auth_id") + .table("user") + .leftJoin("auth", "auth.id", "=", "user.auth_id") + .orderBy("user.id", Desc) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user11" + check t[0]["auth_id"] == newJNull() + + + test("resultIsNull"): + check rdb.table("user").find(50).waitFor().isSome == false + check newSeq[JsonNode](0) == rdb.table("user").where("id", "=", 50).get().waitFor + + +suite($rdb & " insert"): + setup: + setup(rdb) + + test("insert row"): + rdb.table("user").insert(%*{"name": "Alice"}).waitFor + let t = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insert rows"): + rdb.table("user").insert(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + + test("insertId row"): + var id = rdb.table("user").insertId(%*{"name": "Alice"}).waitFor + echo id + let t = rdb.table("user").find(id).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insertId rows"): + let ids = rdb.table("user").insertId(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + + var t = rdb.table("user").find(ids[0]).waitFor().get() + check t["name"].getStr == "Alice" + + t = rdb.table("user").find(ids[1]).waitFor().get() + check t["name"].getStr == "Bob" + + + test("insertNil"): + var id = rdb.table("user").insertId(%*{ + "name": "Alice", + "email": nil, + "address": "" + }) + .waitFor + var res = rdb.table("user").find(id).waitFor + echo res.get + check res.get["email"] == newJNull() + + res = rdb.table("user").where("email", "is", nil).first().waitFor + echo res.get + check res.get["email"] == newJNull() + + + test("insert [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").insert(user).waitFor + + let row = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check row["name"].getStr() == "Alice" + + + test("insert seq[T]"): + type User = object + name:string + + let users = @[ + User(name: "Alice"), + User(name: "Bob"), + ] + + rdb.table("user").insert(users).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + +suite($rdb & " update"): + setup: + setup(rdb) + + test("update"): + rdb.table("user").where("id", "=", 1).update(%*{"name": "Alice"}).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("update [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").where("id", "=", 1).update(user).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + +suite($rdb & " delete"): + setup(rdb) + + test("delete"): + rdb.table("user").delete(1).waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + + test("delete where"): + rdb.table("user").where("name", "=", "user1").delete().waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + +suite($rdb & " rawQuery"): + setup(rdb) + + test("get"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).get().waitFor + echo res + check res[0]["name"].getStr == "user1" + + + test("getPlain"): + let sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).getPlain().waitFor + echo res + check res[0][1] == "user1" + + + test("first"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).first().waitFor().get() + echo res + check res["name"].getStr == "user1" + + + test("firstPlain"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT name FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).firstPlain().waitFor() + echo res + check res[0] == "user1" + + + test("exec"): + var sql = "UPDATE \"user\" SET \"name\" = ? WHERE \"id\" = ?" + rdb.raw(sql, %*["updated user1", 1]).exec().waitFor + sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %[1]).get().waitFor + echo res + check res[0]["name"].getStr == "updated user1" + +setup(rdb) +suite($rdb & " aggregates"): + test("count"): + var t = rdb.table("user").count().waitFor + check t == 10 + + test("max"): + var t = rdb.table("user").max("name").waitFor.get + check t == "user9" + var t2 = rdb.table("user").max("id").waitFor.get + check t2 == "10" + + test("min"): + var t = rdb.table("user").min("name").waitFor.get + check t == "user1" + var t2 = rdb.table("user").min("id").waitFor.get + check t2 == "1" + + test("avg"): + var t = rdb.table("user").avg("id").waitFor.get + check t == 5.5 + + test("sum"): + var t = rdb.table("user").sum("id").waitFor.get + check t == 55.0 + + +suite($rdb & " binary data"): + test("insert binary"): + rdb.create( + table("test", [ + Column.increments("id"), + Column.binary("pic"), + ]) + ) + + let client = newAsyncHttpClient() + let response = client.getContent("https://nim-lang.org/assets/img/twitter_banner.png").waitFor() + let imageStream = newStringStream(response) + let binaryImage = imageStream.readAll() + + let id = rdb.table("test").insertId(%*{"pic": binaryImage}).waitFor() + + var res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + rdb.table("test").where("id","=", id).update(%*{"pic": binaryImage}).waitFor() + + res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/postgres/test_schema.nim b/clients/nim-allographer/tests/postgres/test_schema.nim new file mode 100644 index 0000000..5b25b39 --- /dev/null +++ b/clients/nim-allographer/tests/postgres/test_schema.nim @@ -0,0 +1,332 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -d:reset -r tests/postgres/test_schema.nim + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = postgres +clearTables(rdb).waitFor() + +suite("PostgreSQL create table"): + test("create table"): + rdb.create( + table("IntRelation", [ + Column.increments("id") + ]), + table("StrRelation", [ + Column.uuid("uuid") + ]), + table("TypeIndex", [ + Column.increments("id"), + Column.integer("integer").unsigned().index().default(1), + Column.smallInteger("smallInteger").unsigned().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().index().default(1), + Column.bigInteger("bigInteger").unsigned().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1), + Column.double("double", 10, 3).unsigned().index().default(1.1), + Column.float("float").unsigned().index().default(1.1), + Column.uuid("uuid").index().default("A"), + Column.char("char", 255).index().default("A"), + Column.string("string").index().default("A"), + Column.text("text").index().default("A"), + Column.mediumText("mediumText").index().default("A"), + Column.longText("longText").index().default("A"), + Column.date("date").index().default(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default(), + Column.timestamp("timestamp").index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").index().default("A"), + Column.boolean("boolean").index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A"), + Column.json("json").index().default(%*{"key":"value"}), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]), + table("TypeUnique", [ + Column.increments("id"), + Column.integer("integer").unsigned().unique().index().default(1), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1), + Column.float("float").unsigned().unique().index().default(1.1), + Column.uuid("uuid").unique().index().default("A"), + Column.char("char", 255).unique().index().default("A"), + Column.string("string").unique().index().default("A"), + Column.text("text").unique().index().default("A"), + Column.mediumText("mediumText").unique().index().default("A"), + Column.longText("longText").unique().index().default("A"), + Column.date("date").unique().index().default(), + Column.datetime("datetime").unique().index().default(), + Column.time("time").unique().index().default(), + Column.timestamp("timestamp").unique().index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").unique().index().default("A"), + Column.boolean("boolean").unique().index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A"), + Column.json("json").unique().index().default(%*{"key":"value"}), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]) + ) + + + test("change columns"): + rdb.alter( + table("TypeIndex", [ + Column.integer("integer").unsigned().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().index().default(1.1).change(), + Column.float("float").unsigned().index().default(1.1).change(), + Column.uuid("uuid").index().default("A").change(), + Column.char("char", 255).index().default("A").change(), + Column.string("string").index().default("A").change(), + Column.text("text").index().default("A").change(), + Column.mediumText("mediumText").index().default("A").change(), + Column.longText("longText").index().default("A").change(), + Column.date("date").index().default().change(), + Column.datetime("datetime").index().default().change(), + Column.time("time").index().default().change(), + Column.timestamp("timestamp").index().default().change(), + Column.binary("binary").index().default("A").change(), + Column.boolean("boolean").index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").change(), + Column.json("json").index().default(%*{"key":"value"}).change(), + ]), + table("TypeUnique", [ + Column.integer("integer").unsigned().unique().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.float("float").unsigned().unique().index().default(1.1).change(), + Column.uuid("uuid").unique().index().default("A").change(), + Column.char("char", 255).unique().index().default("A").change(), + Column.string("string").unique().index().default("A").change(), + Column.text("text").unique().index().default("A").change(), + Column.mediumText("mediumText").unique().index().default("A").change(), + Column.longText("longText").unique().index().default("A").change(), + Column.date("date").unique().index().default().change(), + Column.datetime("datetime").unique().index().default().change(), + Column.time("time").unique().index().default().change(), + Column.timestamp("timestamp").unique().index().default().change(), + Column.binary("binary").unique().index().default("A").change(), + Column.boolean("boolean").unique().index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").change(), + Column.json("json").unique().index().default(%*{"key":"value"}).change(), + ]) + ) + + +suite($rdb & " alter table"): + setup: + rdb.create( + table("IntRelation", [ + Column.increments("id"), + ]), + table("StrRelation", [ + Column.uuid("uuid"), + ]), + table("TypeUnique", [ + Column.integer("num"), + Column.string("str"), + ]), + table("TypeIndex", [ + Column.integer("num"), + Column.string("str"), + ]) + ) + + + test("add columns"): + rdb.alter( + table("TypeIndex", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().index().default(1.1).add(), + Column.float("float").unsigned().index().default(1.1).add(), + Column.uuid("uuid").index().default("A").add(), + Column.char("char", 255).index().default("A").add(), + Column.string("string").index().default("A").add(), + Column.text("text").index().default("A").add(), + Column.mediumText("mediumText").index().default("A").add(), + Column.longText("longText").index().default("A").add(), + Column.date("date").index().default().add(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default().add(), + Column.timestamp("timestamp").index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").index().default("A").add(), + Column.boolean("boolean").index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").add(), + Column.json("json").index().default(%*{"key":"value"}).add(), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL).add(), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL).add() + ]), + table("TypeUnique", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().unique().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.float("float").unsigned().unique().index().default(1.1).add(), + Column.uuid("uuid").unique().index().default("A").add(), + Column.char("char", 255).unique().index().default("A").add(), + Column.string("string").unique().index().default("A").add(), + Column.text("text").unique().index().default("A").add(), + Column.mediumText("mediumText").unique().index().default("A").add(), + Column.longText("longText").unique().index().default("A").add(), + Column.date("date").unique().index().default().add(), + Column.datetime("datetime").unique().index().default().add(), + Column.time("time").unique().index().default().add(), + Column.timestamp("timestamp").unique().index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").unique().index().default("A").add(), + Column.boolean("boolean").unique().index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").add(), + Column.json("json").unique().index().default(%*{"key":"value"}).add() + ]) + ) + + + test("rename column"): + rdb.alter( + table("TypeIndex", [ + Column.renameColumn("num", "num2"), + Column.renameColumn("str", "str2"), + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("num") + check columns.contains("num2") + check not columns.contains("str") + check columns.contains("str2") + + + test("drop column"): + rdb.alter( + table("TypeIndex", [ + Column.dropColumn("str") + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("str") + + + test("rename table"): + rdb.drop( + table("TypeIndex_renamed") + ) + rdb.table("TypeIndex").insert(%*{"num":1, "str": "a"}).waitFor + + rdb.alter( + table("TypeIndex").renameTo("TypeIndex_renamed") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + res = rdb.table("TypeIndex_renamed").first().waitFor + check res.isSome + + + test("drop table"): + rdb.create( + table("TypeIndex", [ + Column.integer("num") + ]) + ) + + rdb.drop( + table("TypeIndex") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + +suite($rdb & " primary"): + test("primary key"): + rdb.create([ + table("relation", [ + Column.increments("id"), + ]), + table("TypeIndex", @[ + Column.integer("index1"), + Column.integer("index2"), + Column.string("string"), + Column.foreign("relation_id").reference("id").onTable("relation").onDelete(SET_NULL) + ], + primary = @["index1", "index2"] + ) + ]) + + +suite("Comment"): + test("create table with column comment"): + rdb.create( + table("Comment", [ + Column.integer("index1").comment("index1 comment"), + Column.string("string").comment("string comment") + ]) + ) + + test("alter table change column comment"): + rdb.alter( + table("Comment", [ + Column.integer("index1").comment("changed index1 comment").change() + ]) + ) + + test("alter table add column comment"): + rdb.alter( + table("Comment", [ + Column.string("string2").comment("string2 comment").add() + ]) + ) + + test("alter table add null comment"): + rdb.alter( + table("Comment", [ + Column.string("string").comment(nil).change() + ]) + ) + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/postgres/test_transaction.nim b/clients/nim-allographer/tests/postgres/test_transaction.nim new file mode 100644 index 0000000..4a49bbf --- /dev/null +++ b/clients/nim-allographer/tests/postgres/test_transaction.nim @@ -0,0 +1,131 @@ +discard """ + cmd: "nim c --mm:orc -d:reset -r $file" +""" + +# nim c -r -d:reset tests/v2/postgres/test_transaction.nim + +import std/unittest +import std/json +import std/strformat +import std/strutils +import std/options +import std/asyncdispatch +import std/macros +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = postgres + +proc setUp(rdb:PostgresConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor() + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId + } + ) + + rdb.table("user").insert(users).waitFor() + + +suite("raw code transaction"): + setup: + setUp(rdb) + + test("commit"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + rdb.commit().await + except CatchableError: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor + + + test("rollback"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(CatchableError, "") + except: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor + + +suite("transaction template"): + setup: + setUp(rdb) + + test("commit"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor() + + + test("rollback"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(DbError, "") + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor() + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/sqlite/connections.nim b/clients/nim-allographer/tests/sqlite/connections.nim new file mode 100644 index 0000000..7a9250d --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/connections.nim @@ -0,0 +1,11 @@ +import std/os +import std/strutils +import ../../src/allographer/connection + +let + sqliteHost = getEnv("SQLITE_HOST") + maxConnections = getEnv("DB_MAX_CONNECTION").parseInt + timeout = getEnv("DB_TIMEOUT").parseInt + +echo "sqliteHost: ", sqliteHost +let sqlite* = dbOpen(SQLite3, sqliteHost, maxConnections, timeout, shouldDisplayLog=true) diff --git a/clients/nim-allographer/tests/sqlite/test_create_schema.nim b/clients/nim-allographer/tests/sqlite/test_create_schema.nim new file mode 100644 index 0000000..94c5dd9 --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_create_schema.nim @@ -0,0 +1,107 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -d:reset -r tests/sqlite/test_create_schema.nim + +import std/unittest +import std/asyncdispatch +import std/os +import std/strutils +import std/json +import ../../src/allographer/schema_builder +import ./connections +import ../clear_tables + + +let rdb = sqlite +let schemaFilePath = getCurrentDir() / "schema.nim" + +suite "Schema output after migration": + setup: + # schema.nim ファイルの削除 + if fileExists(schemaFilePath): + removeFile(schemaFilePath) + + rdb.create( + table("int_relation", [ + Column.increments("id") + ]), + table("str_relation", [ + Column.uuid("uuid") + ]), + table("test_schema_output", [ + Column.increments("id"), + Column.integer("integer"), + Column.smallInteger("smallInteger"), + Column.mediumInteger("mediumInteger"), + Column.bigInteger("bigInteger"), + Column.decimal("decimal", 10, 3), + Column.double("double", 10, 3), + Column.float("float"), + Column.uuid("uuid"), + Column.char("char", 255), + Column.string("string"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date"), + Column.datetime("datetime"), + Column.time("time"), + Column.timestamp("timestamp"), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean"), + Column.enumField("enumField", ["A", "B", "C"]), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("int_relation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("str_relation").onDelete(SET_NULL) + ]) + ) + + + test "should generate schema.nim file": + # スキーマ生成 + rdb.createSchema(schemaFilePath).waitFor() + + # schema.nim ファイルの存在確認 + check fileExists(schemaFilePath) + + # ファイル内容の検証 + let schemaContent = readFile(schemaFilePath) + echo "schemaContent: ", schemaContent + check schemaContent.contains("type TestSchemaOutputTable* = object") + check schemaContent.contains("## test_schema_output") + check schemaContent.contains("integer*: int") + check schemaContent.contains("smallInteger*: int") + check schemaContent.contains("mediumInteger*: int") + check schemaContent.contains("bigInteger*: int") + check schemaContent.contains("decimal*: float") + check schemaContent.contains("double*: float") + check schemaContent.contains("float*: float") + check schemaContent.contains("uuid*: string") + check schemaContent.contains("char*: string") + check schemaContent.contains("string*: string") + check schemaContent.contains("text*: string") + check schemaContent.contains("mediumText*: string") + check schemaContent.contains("longText*: string") + check schemaContent.contains("date*: string") + check schemaContent.contains("datetime*: string") + check schemaContent.contains("time*: string") + check schemaContent.contains("timestamp*: string") + check schemaContent.contains("created_at*: string") + check schemaContent.contains("updated_at*: string") + check schemaContent.contains("deleted_at*: string") + check schemaContent.contains("binary*: string") + check schemaContent.contains("boolean*: bool") + check schemaContent.contains("enumField*: string") + check schemaContent.contains("json*: JsonNode") + check schemaContent.contains("int_relation_id*: int") + check schemaContent.contains("str_relation_id*: string") + + +clearTables(rdb).waitFor() +# schema.nim ファイルの削除 +if fileExists(schemaFilePath): + removeFile(schemaFilePath) diff --git a/clients/nim-allographer/tests/sqlite/test_open.nim b/clients/nim-allographer/tests/sqlite/test_open.nim new file mode 100644 index 0000000..2be6d53 --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_open.nim @@ -0,0 +1,21 @@ +discard """ + cmd: "nim c $file" +""" + +import std/os +import std/unittest +import ../../src/allographer/connection + + +suite("SQLite connection"): + test("connection"): + let database = getTempDir() / "allographer-open-test.sqlite3" + let rdb = dbOpen(SQLite3, database, shouldDisplayLog=false) + check(rdb.pools.conns.len > 0) + + + test("connection with DatabaseUrl"): + let database = getTempDir() / "allographer-open-test-url.sqlite3" + let url = asDatabaseUrl("sqlite:///" & database) + let rdb = dbOpen(SQLite3, databaseUrl = url) + check(rdb.pools.conns.len > 0) diff --git a/clients/nim-allographer/tests/sqlite/test_pool_wait.nim b/clients/nim-allographer/tests/sqlite/test_pool_wait.nim new file mode 100644 index 0000000..76ee3bd --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_pool_wait.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +import std/[asyncdispatch, os, strutils, unittest] +import ../../src/allographer/connection +import ../../src/allographer/query_builder + + +suite "SQLite pool waiter (notify)": + test "pool size 1: two concurrent raw gets both complete": + let sqliteHost = getEnv("SQLITE_HOST") + let timeout = getEnv("DB_TIMEOUT").parseInt + let rdb = dbOpen(SQLite3, sqliteHost, maxConnections = 1, timeout) + proc sel(): Future[void] {.async.} = + discard await rdb.raw("SELECT 1").get() + let a = sel() + let b = sel() + waitFor all(a, b) + + test "pool size 0: raw get raises DbError": + let sqliteHost = getEnv("SQLITE_HOST") + let rdb = dbOpen(SQLite3, sqliteHost, maxConnections = 0, timeout = 0) + expect(DbError): + discard waitFor rdb.raw("SELECT 1").get() diff --git a/clients/nim-allographer/tests/sqlite/test_prepared_statement.nim b/clients/nim-allographer/tests/sqlite/test_prepared_statement.nim new file mode 100644 index 0000000..d1d7899 --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_prepared_statement.nim @@ -0,0 +1,149 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections + + +let rdb = sqlite + + +proc setup(rdb: SqliteConnections) = + rdb.create([ + table("auth", [ + Column.increments("id"), + Column.string("auth") + ]), + table("user", [ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]).waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + + +suite($rdb & " prepared statement"): + test("aged connection refresh"): + rdb.pools.maxConnectionLifetime = 1 + rdb.pools.maxConnectionIdleTime = 1 + for conn in rdb.pools.conns.mitems: + conn.createdAt = 0 + conn.lastUsedAt = 0 + + let sql = """SELECT "id", "name", "email", "address" FROM "user" WHERE "id" = ?""" + let stmt = rdb.prepare(sql) + defer: + waitFor stmt.close() + + let rowOpt = stmt.first(@["1"]).waitFor + check rowOpt.isSome + + var refreshed = false + for conn in rdb.pools.conns: + if conn.createdAt > 0: + refreshed = true + break + check refreshed + + test("select"): + let stmt = rdb.prepare("""SELECT "id", "name", "email", "address" FROM "user" WHERE "id" = ?""") + defer: + waitFor stmt.close() + + let args = newJArray() + args.add(newJInt(1)) + + let rows = stmt.get(args).waitFor + check rows.len == 1 + check rows[0] == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address": newJNull()} + let rowOpt = stmt.first(args).waitFor + let row = options.get(rowOpt) + check row["name"].getStr == "user1" + check stmt.getPlain(args).waitFor[0][1] == "user1" + check stmt.firstPlain(args).waitFor[1] == "user1" + + + test("logical close and clear cache"): + let sql = """SELECT "id", "name", "email", "address" FROM "user" WHERE "id" = ?""" + let stmt = rdb.prepare(sql) + discard waitFor stmt.first(@["1"]) + waitFor stmt.close() + + let stmt2 = rdb.prepare(sql) + defer: + waitFor stmt2.close() + + let rowOpt = stmt2.first(@["1"]).waitFor + check rowOpt.isSome + + waitFor rdb.clearStmtCache() + let stmt3 = rdb.prepare(sql) + defer: + waitFor stmt3.close() + check stmt3.first(@["1"]).waitFor.isSome + + + test("with prepared connection context"): + let selectStmt = rdb.prepare("""SELECT "id", "name" FROM "user" WHERE "id" = ?""") + let updateStmt = rdb.prepare("""UPDATE "user" SET "address" = ? WHERE "id" = ?""") + defer: + waitFor selectStmt.close() + waitFor updateStmt.close() + + waitFor rdb.withConn( + proc(ctx: SqlitePreparedContext): Future[void] {.async.} = + discard await selectStmt.first(ctx, @["1"]) + await updateStmt.exec(ctx, @["ctx-address", "1"]) + ) + + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].getStr == "ctx-address" + + + test("update null"): + let stmt = rdb.prepare("""UPDATE "user" SET "address" = ? WHERE "id" = ?""") + defer: + waitFor stmt.close() + + waitFor stmt.exec(@["NULL", "1"]) + let rowOpt = rdb.table("user").find(1).waitFor + let row = options.get(rowOpt) + check row["address"].kind == JNull diff --git a/clients/nim-allographer/tests/sqlite/test_query.nim b/clients/nim-allographer/tests/sqlite/test_query.nim new file mode 100644 index 0000000..3682924 --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_query.nim @@ -0,0 +1,568 @@ +discard """ + cmd: "nim c -d:reset -d:ssl -r $file" +""" + +# nim c -d:reset -d:ssl -r tests/sqlite/test_query.nim + +import std/unittest +import std/asyncdispatch +import std/httpclient +import std/json +import std/options +import std/streams +import std/strformat +import std/strutils +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = sqlite + +# ============================================================================= +# test +# ============================================================================= + +proc setup(rdb:SqliteConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on").nullable(), + Column.datetime("submit_at").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01 00:00:00", + } + ) + + rdb.table("user").insert(users).waitFor + + +setup(rdb) + +suite($rdb & " get"): + test("get"): + let t = rdb.table("user").get().waitFor + let expect = %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1} + echo t[0] + echo expect + check t[0] == expect + + + test("getPlain"): + let t = rdb.table("user").getPlain().waitFor + check t[0] == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("first"): + var t = rdb.table("user").where("name", "=", "user1").first().waitFor().get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("firstPlain"): + var t = rdb.table("user").firstPlain().waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("find"): + var t = rdb.table("user").find(1).waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findString"): + var t = rdb.table("user").find("1").waitFor.get + check t == %*{"id": 1, "name": "user1", "email": "user1@example.com", "address":newJNull(), "submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00", "auth_id": 1} + + + test("findPlain"): + var t = rdb.table("user").findPlain(1).waitFor + check t == @["1", "user1", "user1@example.com", "", "2020-01-01", "2020-01-01 00:00:00", "1"] + + + test("columns"): + let columns = rdb.table("user").columns().waitFor + check columns == @["id", "name", "email", "address", "submit_on", "submit_at", "auth_id"] + + + test("select"): + var t = rdb.select("name", "email").table("user").get().waitFor + check t[0] == %*{"name": "user1", "email": "user1@example.com"} + + + test("selectAs"): + var t = rdb.select("name as user_name", "email").table("user").get().waitFor + check t[0] == %*{"user_name": "user1", "email": "user1@example.com"} + + + test("selectLike"): + var t = rdb.select("email").table("user").where("email", "LIKE", "%10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "user10%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + t = rdb.select("email").table("user").where("email", "LIKE", "%10@example.com%").get().waitFor + check t == @[%*{"email":"user10@example.com"}] + + + test("select count"): + var t = rdb.select("COUNT(id) as count").table("user").get().waitFor + check t[0]["count"].getStr() == "10" + + + test("select max"): + var t = rdb.select("MAX(id) as max").table("user").get().waitFor + check t[0]["max"].getStr() == "10" + + + test("where"): + var t = rdb.table("user").where("auth_id", "=", "1").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("orWhere"): + var t = rdb.table("user").where("auth_id", "=", "1").orWhere("name", "=", "user2").get().waitFor + check t == @[ + %*{"id":1,"name":"user1","email":"user1@example.com","address":newJNull(),"submit_on":"2020-01-01","submit_at":"2020-01-01 00:00:00","auth_id":1}, + %*{"id":2,"name":"user2","email":"user2@example.com","address":newJNull(),"submit_on":"2020-02-01","submit_at":"2020-02-01 00:00:00","auth_id":2}, + %*{"id":3,"name":"user3","email":"user3@example.com","address":newJNull(),"submit_on":"2020-03-01","submit_at":"2020-03-01 00:00:00","auth_id":1}, + %*{"id":5,"name":"user5","email":"user5@example.com","address":newJNull(),"submit_on":"2020-05-01","submit_at":"2020-05-01 00:00:00","auth_id":1}, + %*{"id":7,"name":"user7","email":"user7@example.com","address":newJNull(),"submit_on":"2020-07-01","submit_at":"2020-07-01 00:00:00","auth_id":1}, + %*{"id":9,"name":"user9","email":"user9@example.com","address":newJNull(),"submit_on":"2020-09-01","submit_at":"2020-09-01 00:00:00","auth_id":1} + ] + + + test("whereBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereBetween("id", [6, 9]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user7" + + + test("whereBetweenString"): + var t = rdb + .table("user") + .whereBetween("submit_at", ["2020-01-01 00:00:00", "2020-01-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereBetween("submit_on", ["2020-01-01", "2020-01-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereNotBetween"): + var t = rdb + .select("id", "name") + .table("user") + .where("auth_id", "=", 1) + .whereNotBetween("id", [1, 4]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotBetweenString"): + var t = rdb + .table("user") + .whereNotBetween("submit_at", ["2020-2-1 00:00:00", "2020-12-31 00:00:00"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + t = rdb + .table("user") + .whereNotBetween("submit_on", ["2020-2-1", "2020-12-31"]) + .get() + .waitFor + check t[0]["name"].getStr == "user1" + + + test("whereIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user5" + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + check t[0]["name"].getStr() == "user5" + + + test("whereNotIn"): + var t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("id", @[5, 6, 7]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + t = rdb + .select("id", "name") + .table("user") + .whereBetween("id", [4, 10]) + .whereNotIn("name", @["user5", "user6", "user7"]) + .get() + .waitFor + echo t + check t == @[ + %*{"id":4, "name": "user4"}, + %*{"id":8, "name": "user8"}, + %*{"id":9, "name": "user9"}, + %*{"id":10, "name": "user10"}, + ] + + + test("whereNull"): + setUp(rdb) + rdb.table("user").insert(%*{"email": "user11@example.com"}).waitFor + var t = rdb + .select("id", "name", "email") + .table("user") + .whereNull("name") + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 11 + + + test("groupBy"): + var t = rdb + .table("user") + .groupBy("auth_id") + .max("id") + .waitFor() + .get() + echo t + check t == "11" + + + test("having"): + var t = rdb + .select("id", "name") + .table("user") + .groupBy("auth_id") + .groupBy("id") + .having("auth_id", "=", 1) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 1 + + + test("orderBy"): + var t = rdb.table("user") + .where("auth_id", "is not", nil) + .orderBy("auth_id", Asc) + .orderBy("id", Desc) + .get() + .waitFor + echo t + check t[0]["id"].getInt() == 9 + + + test("join"): + var t = rdb + .select("user.id", "user.name") + .table("user") + .join("auth", "auth.id", "=", "user.auth_id") + .where("auth.id", "=", "2") + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user2" + + + test("leftJoin"): + rdb.table("user").insert(%*{ + "name": "user11" + }) + .waitFor + var t = rdb + .select("user.id", "user.name", "user.auth_id") + .table("user") + .leftJoin("auth", "auth.id", "=", "user.auth_id") + .orderBy("user.id", Desc) + .get() + .waitFor + echo t + check t[0]["name"].getStr() == "user11" + check t[0]["auth_id"] == newJNull() + + + test("resultIsNull"): + check rdb.table("user").find(50).waitFor().isSome == false + check newSeq[JsonNode](0) == rdb.table("user").where("id", "=", 50).get().waitFor + + +suite($rdb & " insert"): + setup: + setup(rdb) + + test("insert row"): + rdb.table("user").insert(%*{"name": "Alice"}).waitFor + let t = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insert rows"): + rdb.table("user").insert(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + + test("insertId row"): + var id = rdb.table("user").insertId(%*{"name": "Alice"}).waitFor + echo id + let t = rdb.table("user").find(id).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insertId rows"): + let ids = rdb.table("user").insertId(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + + var t = rdb.table("user").find(ids[0]).waitFor().get() + check t["name"].getStr == "Alice" + + t = rdb.table("user").find(ids[1]).waitFor().get() + check t["name"].getStr == "Bob" + + + test("insertNil"): + var id = rdb.table("user").insertId(%*{ + "name": "Alice", + "email": nil, + "address": "" + }) + .waitFor + var res = rdb.table("user").find(id).waitFor + echo res.get + check res.get["email"] == newJNull() + + res = rdb.table("user").where("email", "is", nil).first().waitFor + echo res.get + check res.get["email"] == newJNull() + + + test("insert [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").insert(user).waitFor + + let row = rdb.table("user").orderBy("id", Desc).first().waitFor().get() + check row["name"].getStr() == "Alice" + + + test("insert seq[T]"): + type User = object + name:string + + let users = @[ + User(name: "Alice"), + User(name: "Bob"), + ] + + rdb.table("user").insert(users).waitFor + let t = rdb.table("user").orderBy("id", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + +suite($rdb & " update"): + setup: + setup(rdb) + + test("update"): + rdb.table("user").where("id", "=", 1).update(%*{"name": "Alice"}).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("update [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").where("id", "=", 1).update(user).waitFor + var t = rdb.table("user").find(1).waitFor().get() + check t["name"].getStr() == "Alice" + + +suite($rdb & " delete"): + setup(rdb) + + test("delete"): + rdb.table("user").delete(1).waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + + test("delete where"): + rdb.table("user").where("name", "=", "user1").delete().waitFor + check rdb.table("user").find(1).waitFor.isSome == false + + +suite($rdb & " rawQuery"): + setup(rdb) + + test("get"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).get().waitFor + echo res + check res[0]["name"].getStr == "user1" + + + test("getPlain"): + let sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).getPlain().waitFor + echo res + check res[0][1] == "user1" + + + test("first"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).first().waitFor().get() + echo res + check res["name"].getStr == "user1" + + + test("firstPlain"): + echo rdb.table("user").get().waitFor + let sql = &"SELECT name FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %*[1]).firstPlain().waitFor() + echo res + check res[0] == "user1" + + + test("exec"): + var sql = "UPDATE \"user\" SET \"name\" = ? WHERE \"id\" = ?" + rdb.raw(sql, %*["updated user1", 1]).exec().waitFor + sql = &"SELECT * FROM \"user\" WHERE \"id\" = ?" + let res = rdb.raw(sql, %[1]).get().waitFor + echo res + check res[0]["name"].getStr == "updated user1" + + +setup(rdb) +suite($rdb & " aggregates"): + test("count"): + var t = rdb.table("user").count().waitFor + check t == 10 + + test("max"): + var t = rdb.table("user").max("name").waitFor.get + check t == "user9" + var t2 = rdb.table("user").max("id").waitFor.get + check t2 == "10" + + test("min"): + var t = rdb.table("user").min("name").waitFor.get + check t == "user1" + var t2 = rdb.table("user").min("id").waitFor.get + check t2 == "1" + + test("avg"): + var t = rdb.table("user").avg("id").waitFor.get + check t == 5.5 + + test("sum"): + var t = rdb.table("user").sum("id").waitFor.get + check t == 55.0 + + +suite($rdb & " insert binary"): + test("should success"): + rdb.create( + table("test", [ + Column.increments("id"), + Column.binary("pic"), + ]) + ) + + let client = newAsyncHttpClient() + let response = client.getContent("https://nim-lang.org/assets/img/twitter_banner.png").waitFor() + let imageStream = newStringStream(response) + let binaryImage = imageStream.readAll() + + let id = rdb.table("test").insertId(%*{"pic": binaryImage}).waitFor() + + var res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + rdb.table("test").where("id","=", id).update(%*{"pic": binaryImage}).waitFor() + + res = rdb.table("test").find(id).waitFor().get() + check res["pic"].getStr().len > 0 + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/sqlite/test_schema.nim b/clients/nim-allographer/tests/sqlite/test_schema.nim new file mode 100644 index 0000000..3ff016f --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_schema.nim @@ -0,0 +1,298 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = sqlite + +suite("Sqlite create table"): + test("create table"): + rdb.create( + table("IntRelation", [ + Column.increments("id") + ]), + table("StrRelation", [ + Column.uuid("uuid") + ]), + table("TypeIndex", [ + Column.increments("id"), + Column.integer("integer").unsigned().index().default(1), + Column.smallInteger("smallInteger").unsigned().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().index().default(1), + Column.bigInteger("bigInteger").unsigned().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1), + Column.double("double", 10, 3).unsigned().index().default(1.1), + Column.float("float").unsigned().index().default(1.1), + Column.uuid("uuid").index().default("A"), + Column.char("char", 255).index().default("A"), + Column.string("string").index().default("A"), + Column.text("text").index().default("A"), + Column.mediumText("mediumText").index().default("A"), + Column.longText("longText").index().default("A"), + Column.date("date").index().default(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default(), + Column.timestamp("timestamp").index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").index().default("A"), + Column.boolean("boolean").index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A"), + Column.json("json").index().default(%*{"key":"value"}), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]), + table("TypeUnique", [ + Column.increments("id"), + Column.integer("integer").unsigned().unique().index().default(1), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1), + Column.float("float").unsigned().unique().index().default(1.1), + Column.uuid("uuid").unique().index().default("A"), + Column.char("char", 255).unique().index().default("A"), + Column.string("string").unique().index().default("A"), + Column.text("text").unique().index().default("A"), + Column.mediumText("mediumText").unique().index().default("A"), + Column.longText("longText").unique().index().default("A"), + Column.date("date").unique().index().default(), + Column.datetime("datetime").unique().index().default(), + Column.time("time").unique().index().default(), + Column.timestamp("timestamp").unique().index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").unique().index().default("A"), + Column.boolean("boolean").unique().index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A"), + Column.json("json").unique().index().default(%*{"key":"value"}), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL) + ]) + ) + + + test("change columns"): + rdb.alter( + table("TypeIndex", [ + Column.integer("integer").unsigned().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().index().default(1.1).change(), + Column.float("float").unsigned().index().default(1.1).change(), + Column.uuid("uuid").index().default("A").change(), + Column.char("char", 255).index().default("A").change(), + Column.string("string").index().default("A").change(), + Column.text("text").index().default("A").change(), + Column.mediumText("mediumText").index().default("A").change(), + Column.longText("longText").index().default("A").change(), + Column.date("date").index().default().change(), + Column.datetime("datetime").index().default().change(), + Column.time("time").index().default().change(), + Column.timestamp("timestamp").index().default().change(), + Column.binary("binary").index().default("A").change(), + Column.boolean("boolean").index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").change(), + Column.json("json").index().default(%*{"key":"value"}).change(), + ]), + table("TypeUnique", [ + Column.integer("integer").unsigned().unique().index().default(1).change(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).change(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).change(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).change(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).change(), + Column.float("float").unsigned().unique().index().default(1.1).change(), + Column.uuid("uuid").unique().index().default("A").change(), + Column.char("char", 255).unique().index().default("A").change(), + Column.string("string").unique().index().default("A").change(), + Column.text("text").unique().index().default("A").change(), + Column.mediumText("mediumText").unique().index().default("A").change(), + Column.longText("longText").unique().index().default("A").change(), + Column.date("date").unique().index().default().change(), + Column.datetime("datetime").unique().index().default().change(), + Column.time("time").unique().index().default().change(), + Column.timestamp("timestamp").unique().index().default().change(), + Column.binary("binary").unique().index().default("A").change(), + Column.boolean("boolean").unique().index().default(true).change(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").change(), + Column.json("json").unique().index().default(%*{"key":"value"}).change(), + ]) + ) + + +suite($rdb & " alter table"): + setup: + rdb.create( + table("IntRelation", [ + Column.increments("id"), + ]), + table("StrRelation", [ + Column.uuid("uuid"), + ]), + table("TypeUnique", [ + Column.integer("num"), + Column.string("str"), + ]), + table("TypeIndex", [ + Column.integer("num"), + Column.string("str"), + ]) + ) + + + test("add columns"): + rdb.alter( + table("TypeIndex", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().index().default(1.1).add(), + Column.float("float").unsigned().index().default(1.1).add(), + Column.uuid("uuid").index().default("A").add(), + Column.char("char", 255).index().default("A").add(), + Column.string("string").index().default("A").add(), + Column.text("text").index().default("A").add(), + Column.mediumText("mediumText").index().default("A").add(), + Column.longText("longText").index().default("A").add(), + Column.date("date").index().default().add(), + Column.datetime("datetime").index().default(), + Column.time("time").index().default().add(), + Column.timestamp("timestamp").index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").index().default("A").add(), + Column.boolean("boolean").index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").add(), + Column.json("json").index().default(%*{"key":"value"}).add(), + Column.foreign("int_relation_id").reference("id").onTable("IntRelation").onDelete(SET_NULL).add(), + Column.strForeign("str_relation_id").reference("uuid").onTable("StrRelation").onDelete(SET_NULL).add() + ]), + table("TypeUnique", [ + Column.increments("id").add(), + Column.integer("integer").unsigned().unique().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().unique().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().unique().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().unique().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().unique().index().default(1.1).add(), + Column.float("float").unsigned().unique().index().default(1.1).add(), + Column.uuid("uuid").unique().index().default("A").add(), + Column.char("char", 255).unique().index().default("A").add(), + Column.string("string").unique().index().default("A").add(), + Column.text("text").unique().index().default("A").add(), + Column.mediumText("mediumText").unique().index().default("A").add(), + Column.longText("longText").unique().index().default("A").add(), + Column.date("date").unique().index().default().add(), + Column.datetime("datetime").unique().index().default().add(), + Column.time("time").unique().index().default().add(), + Column.timestamp("timestamp").unique().index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").unique().index().default("A").add(), + Column.boolean("boolean").unique().index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).unique().index().default("A").add(), + Column.json("json").unique().index().default(%*{"key":"value"}).add() + ]) + ) + + + test("rename column"): + rdb.alter( + table("TypeIndex", [ + Column.renameColumn("num", "num2"), + Column.renameColumn("str", "str2"), + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("num") + check columns.contains("num2") + check not columns.contains("str") + check columns.contains("str2") + + + test("drop column"): + rdb.alter( + table("TypeIndex", [ + Column.dropColumn("str") + ]) + ) + + let columns = rdb.table("TypeIndex").columns().waitFor + check not columns.contains("str") + + + test("rename table"): + rdb.drop( + table("TypeIndex_renamed") + ) + rdb.table("TypeIndex").insert(%*{"num":1, "str": "a"}).waitFor + + rdb.alter( + table("TypeIndex").renameTo("TypeIndex_renamed") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + res = rdb.table("TypeIndex_renamed").first().waitFor + check res.isSome + + + test("drop table"): + rdb.create( + table("TypeIndex", [ + Column.integer("num") + ]) + ) + + rdb.drop( + table("TypeIndex") + ) + + var res:Option[JsonNode] + try: + res = rdb.table("TypeIndex").first().waitFor + check not res.isSome + except CatchableError: + check true + + +suite($rdb & " primary"): + test("primary key"): + rdb.create( + table("relation", [ + Column.increments("id"), + ]), + table("TypeIndex", @[ + Column.integer("index1"), + Column.integer("index2"), + Column.string("string"), + Column.foreign("relation_id").reference("id").onTable("relation").onDelete(SET_NULL) + ], + primary = @["index1", "index2"] + ) + ) + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/sqlite/test_schema_length_check.nim b/clients/nim-allographer/tests/sqlite/test_schema_length_check.nim new file mode 100644 index 0000000..ba850bd --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_schema_length_check.nim @@ -0,0 +1,88 @@ +discard """ + cmd: "nim c -d:reset $file" +""" +# nim c -r -d:reset tests/sqlite/test_schema_length_check.nim + +import std/unittest +import std/asyncdispatch +import std/json +import std/strformat +import std/strutils +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = sqlite + + +proc tableSql(tableName:string): string = + let rows = rdb.raw(&"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = '{tableName}'").get().waitFor + return rows[0]["sql"].getStr + + +suite("Sqlite length check constraint"): + test("create table uses a column reference in CHECK"): + let tableName = "length_check_create" + + rdb.create( + table(tableName, [ + Column.increments("id"), + Column.char("char", 4).default("ABCD"), + Column.string("string", 4).nullable(), + Column.uuid("uuid").nullable() + ]) + ) + + let sql = tableSql(tableName) + check sql.contains("DEFAULT 'ABCD'") + check sql.contains("CHECK (length(\"char\") <= 4)") + check sql.contains("CHECK (length(\"string\") <= 4)") + check sql.contains("CHECK (length(\"uuid\") <= 256)") + + expect DbError: + rdb.table(tableName).insert(@[ + %*{ + "char": "ABCDE", + "string": "ABCDE", + "uuid": repeat("u", 300) + } + ]).waitFor + + + test("add column keeps CHECK on a column reference"): + let tableName = "length_check_add" + + rdb.create( + table(tableName, [ + Column.increments("id") + ]) + ) + + rdb.alter( + table(tableName, [ + Column.char("char", 4).nullable().add(), + Column.string("string", 4).default("WXYZ").add(), + Column.uuid("uuid").default("ZZZZ").add() + ]) + ) + + let sql = tableSql(tableName) + check sql.contains("CHECK (length(\"char\") <= 4)") + check sql.contains("CHECK (length(\"string\") <= 4)") + check sql.contains("CHECK (length(\"uuid\") <= 256)") + check sql.contains("DEFAULT 'WXYZ'") + check sql.contains("DEFAULT 'ZZZZ'") + + expect DbError: + rdb.table(tableName).insert(@[ + %*{ + "char": "ABCDE", + "string": "ABCDE", + "uuid": repeat("u", 300) + } + ]).waitFor + + +clearTables(rdb).waitFor diff --git a/clients/nim-allographer/tests/sqlite/test_transaction.nim b/clients/nim-allographer/tests/sqlite/test_transaction.nim new file mode 100644 index 0000000..0e8360e --- /dev/null +++ b/clients/nim-allographer/tests/sqlite/test_transaction.nim @@ -0,0 +1,128 @@ +discard """ + cmd: "nim c --mm:orc -d:reset -r $file" +""" + +import std/unittest +import std/json +import std/strformat +import std/strutils +import std/options +import std/asyncdispatch +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = sqlite + +proc setUp(rdb:SqliteConnections) = + rdb.create([ + table("auth",[ + Column.increments("id"), + Column.string("auth") + ]), + table("user",[ + Column.increments("id"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.foreign("auth_id").reference("id").onTable("auth").onDelete(SET_NULL) + ]) + ]) + + # seeder + seeder(rdb, "auth"): + rdb.table("auth").insert(@[ + %*{"auth": "admin"}, + %*{"auth": "user"} + ]) + .waitFor() + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: 2 else: 1 + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "auth_id": authId + } + ) + + rdb.table("user").insert(users).waitFor() + + +suite("transaction"): + suite("raw code"): + setup: + setUp(rdb) + + test("commit"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + rdb.commit().await + except DbError, CatchableError: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor + + + test("rollback"): + (proc() {.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + block: + rdb.begin().await + try: + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(CatchableError, "") + except DbError, CatchableError: + rdb.rollback().await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor + + suite("transaction template"): + setup: + setUp(rdb) + + test("commit"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "updated" + )().waitFor() + + + test("rollback"): + (proc(){.async.} = + var user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + + transaction(rdb): + rdb.table("user").where("id", "=", 1).update(%*{"name": "updated"}).await + raise newException(CatchableError, "") + + user1 = rdb.table("user").find(1).await.get() + check user1["name"].getStr() == "user1" + )().waitFor() + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/surrealdb/connection.nim b/clients/nim-allographer/tests/surrealdb/connection.nim new file mode 100644 index 0000000..2fd9e2a --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/connection.nim @@ -0,0 +1,27 @@ +import std/asyncdispatch +import std/os +import std/strutils +import ../../src/allographer/connection + +proc envStringDefault(key, defaultValue: string): string = + let value = getEnv(key) + if value.len == 0: + return defaultValue + return value + +proc envIntDefault(key: string, defaultValue: int): int = + let value = getEnv(key) + if value.len == 0: + return defaultValue + return value.parseInt + +let + database = envStringDefault("DB_DATABASE", "test") + user = envStringDefault("DB_USER", "user") + password = envStringDefault("DB_PASSWORD", "pass") + surrealHost = envStringDefault("SURREAL_HOST", "http://surreal") + surrealPort = envIntDefault("SURREAL_PORT", 8000) + maxConnections = envIntDefault("DB_MAX_CONNECTION", 5) + timeout = envIntDefault("DB_TIMEOUT", 30) + +let surreal* = dbOpen(SurrealDB, database, database, user, password, surrealHost, surrealPort, maxConnections, timeout, shouldDisplayLog=true).waitFor() diff --git a/clients/nim-allographer/tests/surrealdb/connections.nim b/clients/nim-allographer/tests/surrealdb/connections.nim new file mode 100644 index 0000000..317d64f --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/connections.nim @@ -0,0 +1,16 @@ +import std/asyncdispatch +import std/os +import std/strutils +import ../../src/allographer/connection + +proc envIntDefault(key: string, defaultValue: int): int = + let value = getEnv(key) + if value.len == 0: + return defaultValue + return value.parseInt + +let + maxConnections = envIntDefault("DB_MAX_CONNECTION", 5) + timeout = envIntDefault("DB_TIMEOUT", 30) + +let surreal* = dbOpen(SurrealDB, "test", "test", "user", "pass", "http://surreal", 8000, maxConnections, timeout, shouldDisplayLog=true).waitFor() diff --git a/clients/nim-allographer/tests/surrealdb/test_autoincrements.nim b/clients/nim-allographer/tests/surrealdb/test_autoincrements.nim new file mode 100644 index 0000000..5acf4b1 --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/test_autoincrements.nim @@ -0,0 +1,97 @@ +discard """ + cmd: "nim c -d:reset -d:ssl $file" +""" +# nim c -r -d:reset -d:ssl tests/surrealdb/test_autoincrements.nim + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import ../../src/allographer/query_builder +import ../../src/allographer/schema_builder +import ./connections +import ../clear_tables + + +let rdb = surreal + +suite("autoincrements"): + test("sequence table is not exists"): + rdb.raw("REMOVE TABLE _autoincrement_sequences").exec().waitFor() + + var info = rdb.raw("INFO FOR DB").info().waitFor() + echo "info: ", info.pretty + check not info[0]["result"]["tables"].contains("_autoincrement_sequences") + + rdb.create( + table("user", [ + Column.increments("index"), + Column.string("name") + ]) + ) + + info = rdb.raw("INFO FOR DB").info().waitFor() + echo "info: ", info.pretty + check info[0]["result"]["tables"].contains("_autoincrement_sequences") + + let id = rdb.table("user").insertId(%*{"name": "alice"}).waitFor() + var user = rdb.table("user").find(id).waitFor().get() + check user["name"].getStr == "alice" + check user["index"].getInt == 1 + + rdb.update(id, %*{"name": "updated"}).waitFor() + + user = rdb.table("user").find(id).waitFor().get() + check user["name"].getStr == "updated" + check user["index"].getInt == 1 + + + test("sequence table is exists"): + var info = rdb.raw("INFO FOR DB").info().waitFor() + check info[0]["result"]["tables"].contains("_autoincrement_sequences") + + rdb.create( + table("user", [ + Column.increments("index"), + Column.string("name") + ]) + ) + + let id = rdb.table("user").insertId(%*{"name": "alice"}).waitFor() + var user = rdb.table("user").find(id).waitFor().get() + check user["name"].getStr == "alice" + check user["index"].getInt == 1 + + rdb.update(id, %*{"name": "updated"}).waitFor() + + user = rdb.table("user").find(id).waitFor().get() + check user["name"].getStr == "updated" + check user["index"].getInt == 1 + + + test("cycle"): + rdb.create( + table("user", [ + Column.increments("index"), + Column.string("name") + ]) + ) + + var users: seq[JsonNode] + for i in 1..100: + users.add(%*{"name": &"user{i}"}) + rdb.table("user").insert(users).waitFor() + + var user = rdb.table("user").where("index", "=", 1).first().waitFor().get() + check user["index"].getInt == 1 + + user = rdb.table("user").where("index", "=", 50).first().waitFor().get() + check user["index"].getInt == 50 + + user = rdb.table("user").where("index", "=", 100).first().waitFor().get() + check user["index"].getInt == 100 + + echo rdb.table("user").orderBy("index", Asc).limit(20).get().waitFor() + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/surrealdb/test_create_schema.nim b/clients/nim-allographer/tests/surrealdb/test_create_schema.nim new file mode 100644 index 0000000..31b667b --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/test_create_schema.nim @@ -0,0 +1,106 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -d:reset -r tests/surrealdb/test_create_schema.nim + +import std/unittest +import std/asyncdispatch +import std/os +import std/strutils +import std/json +import ../../src/allographer/schema_builder +import ./connections +import ../clear_tables + + +let rdb = surreal +let schemaFilePath = getCurrentDir() / "schema.nim" +clearTables(rdb).waitFor() + +suite "Schema output after migration": + setup: + # schema.nim ファイルの削除 + if fileExists(schemaFilePath): + removeFile(schemaFilePath) + + rdb.create( + table("int_relation", [ + Column.increments("index") + ]), + table("str_relation", [ + Column.uuid("uuid") + ]), + table("test_schema_output", [ + Column.increments("index"), + Column.integer("integer"), + Column.smallInteger("smallInteger"), + Column.mediumInteger("mediumInteger"), + Column.bigInteger("bigInteger"), + Column.decimal("decimal", 10, 3), + Column.double("double", 10, 3), + Column.float("float"), + Column.uuid("uuid"), + Column.char("char", 255), + Column.string("string"), + Column.text("text"), + Column.mediumText("mediumText"), + Column.longText("longText"), + Column.date("date"), + Column.datetime("datetime"), + Column.timestamp("timestamp"), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary"), + Column.boolean("boolean"), + Column.enumField("enumField", ["A", "B", "C"]), + Column.json("json"), + Column.foreign("int_relation_id").reference("id").onTable("int_relation").onDelete(SET_NULL), + Column.strForeign("str_relation_id").reference("uuid").onTable("str_relation").onDelete(SET_NULL) + ]) + ) + + + test "should generate schema.nim file": + # スキーマ生成 + rdb.createSchema(schemaFilePath).waitFor() + + # schema.nim ファイルの存在確認 + check fileExists(schemaFilePath) + + # ファイル内容の検証 + let schemaContent = readFile(schemaFilePath) + echo "schemaContent: ", schemaContent + check schemaContent.contains("type TestSchemaOutputTable* = object") + check schemaContent.contains("## test_schema_output") + check schemaContent.contains("integer*: int") + check schemaContent.contains("smallInteger*: int") + check schemaContent.contains("mediumInteger*: int") + check schemaContent.contains("bigInteger*: int") + check schemaContent.contains("decimal*: float") + check schemaContent.contains("double*: float") + check schemaContent.contains("float*: float") + check schemaContent.contains("uuid*: string") + check schemaContent.contains("char*: string") + check schemaContent.contains("string*: string") + check schemaContent.contains("text*: string") + check schemaContent.contains("mediumText*: string") + check schemaContent.contains("longText*: string") + check schemaContent.contains("date*: string") + check schemaContent.contains("datetime*: string") + check schemaContent.contains("timestamp*: string") + check schemaContent.contains("created_at*: string") + check schemaContent.contains("updated_at*: string") + check schemaContent.contains("deleted_at*: string") + check schemaContent.contains("binary*: string") + check schemaContent.contains("boolean*: bool") + check schemaContent.contains("enumField*: string") + check schemaContent.contains("json*: JsonNode") + check schemaContent.contains("int_relation_id*: string") + check schemaContent.contains("str_relation_id*: string") + + +clearTables(rdb).waitFor() +# schema.nim ファイルの削除 +if fileExists(schemaFilePath): + removeFile(schemaFilePath) diff --git a/clients/nim-allographer/tests/surrealdb/test_lib.nim b/clients/nim-allographer/tests/surrealdb/test_lib.nim new file mode 100644 index 0000000..1cf2189 --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/test_lib.nim @@ -0,0 +1,26 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +import std/json +import std/unittest +import ../../src/allographer/query_builder/libs/surreal/surreal_lib + + +suite "SurrealDB lib helpers": + test "numToAlphabet": + check numToAlphabet(1) == "a" + check numToAlphabet(26) == "z" + check numToAlphabet(27) == "aa" + + test "questionToDaller": + check questionToDaller("SELECT * FROM user WHERE name = ? AND age = ?") == + "SELECT * FROM user WHERE name = $a AND age = $b" + + test "dbFormat json args": + let sql = dbFormat( + "SELECT * FROM user WHERE name = ? AND age = ?", + %*["alice", 42] + ) + check sql == + "LET $a = \"alice\"; LET $b = 42; SELECT * FROM user WHERE name = $a AND age = $b" diff --git a/clients/nim-allographer/tests/surrealdb/test_pool_wait.nim b/clients/nim-allographer/tests/surrealdb/test_pool_wait.nim new file mode 100644 index 0000000..27b4f41 --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/test_pool_wait.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +import std/[asyncdispatch, os, strutils, unittest] +import ../../src/allographer/connection +import ../../src/allographer/query_builder + +proc envIntDefault(key: string, defaultValue: int): int = + let value = getEnv(key) + if value.len == 0: + return defaultValue + return value.parseInt + + +suite "SurrealDB pool waiter (notify)": + test "pool size 1: two concurrent raw gets both complete": + let timeout = envIntDefault("DB_TIMEOUT", 30) + let rdb = dbOpen(SurrealDB, "test", "test", "user", "pass", "http://surreal", 8000, + maxConnections = 1, timeout).waitFor() + proc sel(): Future[void] {.async.} = + discard await rdb.raw("RETURN 1;").get() + let a = sel() + let b = sel() + waitFor all(a, b) diff --git a/clients/nim-allographer/tests/surrealdb/test_prepared_statement.nim b/clients/nim-allographer/tests/surrealdb/test_prepared_statement.nim new file mode 100644 index 0000000..3b9ca49 --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/test_prepared_statement.nim @@ -0,0 +1,98 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +import std/asyncdispatch +import std/deques +import std/json +import std/tables +import std/unittest +import ../../src/allographer/query_builder/libs/surreal/surreal_lib +import ../../src/allographer/query_builder/libs/surreal/surreal_rdb +import ../../src/allographer/query_builder/log +import ../../src/allographer/query_builder/models/surreal/surreal_exec +import ../../src/allographer/query_builder/models/surreal/surreal_types + + +proc newTestSurreal(): SurrealConnections = + let pools = Connections( + conns: @[ + Connection( + conn: SurrealConn(), + isBusy: false, + createdAt: 0, + ) + ], + timeout: 30, + waiters: initDeque[Future[void]](), + preparedCache: initTable[string, SurrealPreparedEntry](), + ) + result = SurrealConnections( + log: LogSetting( + shouldDisplayLog: false, + shouldOutputLogFile: false, + logDir: "", + ), + pools: pools, + ) + + +suite "SurrealDB prepared statement": + test "dbFormatPrepared": + var args = newJArray() + args.add(%*"user:alice") + args.add(%*"2026-04-03T00:00:00Z") + args.add(newJNull()) + let sql = dbFormatPrepared( + questionToDaller("""SELECT * FROM "user" WHERE "id" = ? AND "submit_at" >= ? AND "address" IS ?"""), + args + ) + check sql == + """LET $a = user:alice; LET $b = "2026-04-03T00:00:00Z"; LET $c = NONE; SELECT * FROM "user" WHERE "id" = $a AND "submit_at" >= $b AND "address" IS $c""" + + + test "prepare cache lifecycle": + let rdb = newTestSurreal() + let sql = """SELECT * FROM "user" WHERE "id" = ?""" + + let stmt1 = rdb.prepare(sql) + check stmt1.nArgs == 1 + check stmt1.entry.normalizedSql == questionToDaller(sql) + check stmt1.entry.refCount == 1 + check rdb.pools.preparedCache.len == 1 + + let stmt2 = rdb.prepare(sql) + check stmt2.entry == stmt1.entry + check stmt1.entry.refCount == 2 + + waitFor stmt1.close() + check stmt1.isClosed + check stmt1.entry.refCount == 1 + + waitFor stmt2.close() + check stmt2.isClosed + check stmt2.entry.refCount == 0 + + let stmt4 = rdb.prepare(sql) + check rdb.pools.preparedCache.len == 1 + waitFor rdb.flushStmt(stmt4) + check stmt4.isClosed + check rdb.pools.preparedCache.len == 0 + + let stmt3 = rdb.prepare(sql) + check stmt3.entry != stmt1.entry + waitFor stmt3.close() + waitFor rdb.clearStmtCache() + check rdb.pools.preparedCache.len == 0 + + + test "withConn context": + let rdb = newTestSurreal() + + waitFor rdb.withConn( + proc(ctx: SurrealPreparedContext): Future[void] {.async.} = + check ctx.owner == rdb + check ctx.connI == 0 + ) + + check not rdb.pools.conns[0].isBusy diff --git a/clients/nim-allographer/tests/surrealdb/test_query.nim b/clients/nim-allographer/tests/surrealdb/test_query.nim new file mode 100644 index 0000000..6ec8a12 --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/test_query.nim @@ -0,0 +1,507 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -r -d:reset tests/surrealdb/test_query.nim + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strformat +import std/times +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +let rdb = surreal + + +proc isNullish(node: JsonNode, key: string): bool = + not node.hasKey(key) or node[key].kind == JNull + +# ============================================================================= +# test +# ============================================================================= + +proc setup(rdb:SurrealConnections) = + # rdb.raw("REMOVE TABLE auth; REMOVE TABLE user").exec().waitFor + # rdb.raw("DEFINE TABLE auth SCHEMAFULL; DEFINE TABLE user SCHEMAFULL").exec().waitFor + rdb.create([ + table("auth",[ + Column.increments("index"), + Column.string("name") + ]), + table("user",[ + Column.increments("index"), + Column.string("name").nullable(), + Column.string("email").nullable(), + Column.string("address").nullable(), + Column.date("submit_on"), + Column.datetime("submit_at"), + Column.foreign("auth").reference("id").onTable("auth").onDelete(SET_NULL).nullable() + ]) + ]) + + # seeder + var adminId, userId:SurrealId + seeder(rdb, "auth"): + adminId = rdb.table("auth").insertId(%*{"name":"admin"}).waitFor() + userId = rdb.table("auth").insertId(%*{"name":"user"}).waitFor() + + seeder(rdb, "user"): + var users: seq[JsonNode] + for i in 1..10: + let authId = if i mod 2 == 0: userId else: adminId + let month = if i > 9: $i else: &"0{i}" + users.add( + %*{ + "name": &"user{i}", + "email": &"user{i}@example.com", + "address": newJNull(), + "auth": authId, + "submit_on": &"2020-{month}-01", + "submit_at": &"2020-{month}-01T00:00:00Z", + } + ) + + rdb.table("user").insert(users).waitFor() + + +setup(rdb) + +suite($rdb & " get"): + test("get"): + let t = rdb.table("user").orderBy("name", Asc).get().waitFor() + echo t[0] + check t[0]["index"].getInt() == 1 + check t[0]["name"].getStr() == "user1" + check t[0]["email"].getStr() == "user1@example.com" + check isNullish(t[0], "address") + check t[0]["submit_on"].getStr().parse("yyyy-MM-dd'T'hh:mm:ss'Z'").format("yyyy-MM-dd") == "2020-01-01" + check t[0]["submit_at"].getStr() == "2020-01-01T00:00:00Z" + + + test("first"): + var t = rdb.table("user").where("name", "=", "user1").orderBy("name", Asc).first().waitFor().get() + check t["index"].getInt() == 1 + check t["name"].getStr() == "user1" + check t["email"].getStr() == "user1@example.com" + check isNullish(t, "address") + check t["submit_on"].getStr().parse("yyyy-MM-dd'T'hh:mm:ss'Z'").format("yyyy-MM-dd") == "2020-01-01" + check t["submit_at"].getStr() == "2020-01-01T00:00:00Z" + + + test("find"): + var t = rdb.table("user").where("name", "=", "user1").orderBy("name", Asc).first().waitFor().get() + let id = SurrealId.new(t["id"].getStr()) + t = rdb.table("user").find(id).waitFor().get() + check t["index"].getInt() == 1 + check t["name"].getStr() == "user1" + check t["email"].getStr() == "user1@example.com" + check isNullish(t, "address") + check t["submit_on"].getStr().parse("yyyy-MM-dd'T'hh:mm:ss'Z'").format("yyyy-MM-dd") == "2020-01-01" + check t["submit_at"].getStr() == "2020-01-01T00:00:00Z" + + + test("columns"): + let columns = rdb.table("user").columns().waitFor() + check columns == @["address", "auth", "email", "index", "name", "submit_at", "submit_on"] + + + test("select"): + var t = rdb.select("name", "email").table("user").orderBy("name", Asc).get().waitFor() + check t[0] == %*{"name": "user1", "email": "user1@example.com"} + + + test("selectAs"): + var t = rdb.select("name as user_name", "email").table("user").orderBy("user_name", Asc).get().waitFor() + check t[0] == %*{"user_name": "user1", "email": "user1@example.com"} + + + test("selectLike"): + let users = rdb.table("user").where("email", "CONTAINS", "10").get().waitFor() + check users[0]["email"].getStr() == "user10@example.com" + + var user10 = rdb.raw("SELECT * FROM user WHERE string::starts_with(email, \"user10\")").first().waitFor() + check user10.get()["email"].getStr() == "user10@example.com" + + user10 = rdb.raw("SELECT * FROM user WHERE string::ends_with(email, \"10@example.com\")").first().waitFor() + check user10.get()["email"].getStr() == "user10@example.com" + + + test("select count"): + var t = rdb.raw("SELECT count(`id`) as `count` FROM `user` GROUP ALL").get().waitFor + echo t + check t[0]["count"].getInt() == 10 + + + test("select max"): + var t = rdb.raw("SELECT math::max(`index`) as `max` FROM `user` GROUP ALL").get().waitFor + echo t + check t[0]["max"].getInt() == 10 + + + test("where string"): + let admin = rdb.table("auth").where("name", "=", "admin").first().waitFor().get() + let adminId = SurrealId.new(admin["id"].getStr()) + + var t = rdb.table("user").where("auth", "=", adminId.rawId()).orderBy("index", Asc).get().waitFor() + const ids = [1,3,5,7,9] + for i, row in t: + check row["index"].getInt() == ids[i] + check row["name"].getStr() == &"user{ids[i]}" + check row["email"].getStr() == &"user{ids[i]}@example.com" + check isNullish(row, "address") + check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z" + + + test("where int"): + var t = rdb.table("user").where("index", "=", 1).orderBy("index", Asc).first().waitFor().get() + check t["index"].getInt() == 1 + check t["name"].getStr() == "user1" + check t["email"].getStr() == "user1@example.com" + check isNullish(t, "address") + check t["submit_on"].getStr() == "2020-01-01T00:00:00Z" + + + test("orWhere"): + let admin = rdb.table("auth").where("name", "=", "admin").first().waitFor().get() + let adminId = SurrealId.new(admin["id"].getStr()) + + var t = rdb.table("user").where("auth", "=", adminId).orWhere("name", "=", "user2").orderBy("index", Asc).get().waitFor() + const ids = [1,2,3,5,7,9] + for i, row in t: + check row["index"].getInt() == ids[i] + check row["name"].getStr() == &"user{ids[i]}" + check row["email"].getStr() == &"user{ids[i]}@example.com" + check isNullish(row, "address") + check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z" + + + test("whereBetween"): + var t = rdb + .table("user") + .whereBetween("index", [6, 9]) + .orderBy("index", Asc) + .get() + .waitFor() + + const ids = [6,7,8,9] + for i, row in t: + check row["index"].getInt() == ids[i] + check row["name"].getStr() == &"user{ids[i]}" + check row["email"].getStr() == &"user{ids[i]}@example.com" + check isNullish(row, "address") + check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z" + + + test("whereNotBetween"): + var t = rdb + .table("user") + .whereNotBetween("index", [2, 6]) + .orderBy("index", Asc) + .get() + .waitFor() + + const ids = [1,7,8,9] + for i, row in t: + check row["index"].getInt() == ids[i] + check row["name"].getStr() == &"user{ids[i]}" + check row["email"].getStr() == &"user{ids[i]}@example.com" + check isNullish(row, "address") + check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z" + + + test("whereIn"): + var t = rdb + .table("user") + .whereIn("index", @[5, 6, 7]) + .orderBy("index", Asc) + .get() + .waitFor() + + const ids = [5,6,7] + for i, row in t: + check row["index"].getInt() == ids[i] + check row["name"].getStr() == &"user{ids[i]}" + check row["email"].getStr() == &"user{ids[i]}@example.com" + check isNullish(row, "address") + check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z" + + + test("whereNotIn"): + var t = rdb + .table("user") + .whereNotIn("index", @[4, 5, 6, 7, 10]) + .orderBy("index", Asc) + .get() + .waitFor() + + const ids = [1,2,3,8,9] + for i, row in t: + check row["index"].getInt() == ids[i] + check row["name"].getStr() == &"user{ids[i]}" + check row["email"].getStr() == &"user{ids[i]}@example.com" + check isNullish(row, "address") + check row["submit_on"].getStr() == &"2020-0{ids[i]}-01T00:00:00Z" + + + test("whereNull"): + rdb.table("user").insert(%*{"email": "user11@example.com"}).waitFor() + var t = rdb + .table("user") + .whereNull("name") + .first() + .waitFor() + check t.get()["index"].getInt() == 11 + + setUp(rdb) + + # TODO: not work + # test("groupBy"): + # var t = rdb + # .select("auth.name") + # .table("user") + # .groupBy("auth.name") + # .get() + # .waitFor() + # echo t + + + test("orderBy"): + var res = rdb.table("user").orderBy("name", Asc).get().waitFor() + check res[0]["name"].getStr() == "user1" + + res = rdb.table("user").orderBy("name", Desc).get().waitFor() + check res[0]["name"].getStr() == "user9" + + res = rdb.table("user").orderBy("index", Numeric, Asc).get().waitFor() + check res[0]["name"].getStr() == "user1" + + res = rdb.table("user").orderBy("index", Numeric, Desc).get().waitFor() + check res[0]["name"].getStr() == "user10" + + res = rdb.table("user").orderBy("name", Collate, Asc).get().waitFor() + check res[0]["name"].getStr() == "user1" + + res = rdb.table("user").orderBy("name", Collate, Desc).get().waitFor() + check res[0]["name"].getStr() == "user9" + + + test("fetch"): + var res = rdb.table("user").where("name", "=", "user1").fetch("auth").first().waitFor().get() + check res["auth"]["name"].getStr() == "admin" + res = rdb.table("user").where("name", "=", "user2").fetch("auth").first().waitFor().get() + check res["auth"]["name"].getStr() == "user" + + + test("resultIsNull"): + check rdb.table("user").where("id", "=", 50).first().waitFor().isSome() == false + check newSeq[JsonNode](0) == rdb.table("user").where("id", "=", 50).get().waitFor() + + +suite($rdb & " insert"): + setup: + setup(rdb) + + test("insert row"): + rdb.table("user").insert(%*{"name": "Alice"}).waitFor + let t = rdb.table("user").orderBy("index", Desc).first().waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insert rows"): + rdb.table("user").insert(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + let t = rdb.table("user").orderBy("index", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + + test("insertId row"): + var id = rdb.table("user").insertId(%*{"name": "Alice"}).waitFor + echo id + let t = rdb.table("user").find(id).waitFor().get() + check t["name"].getStr() == "Alice" + + + test("insertId rows"): + let ids = rdb.table("user").insertId(@[ + %*{"name": "Alice"}, + %*{"name": "Bob"}, + ]).waitFor + + var t = rdb.table("user").find(ids[0]).waitFor().get() + check t["name"].getStr == "Alice" + + t = rdb.table("user").find(ids[1]).waitFor().get() + check t["name"].getStr == "Bob" + + + test("insertNil"): + var id = rdb.table("user").insertId(%*{ + "name": "Alice", + "email": nil, + "address": "" + }) + .waitFor + + var res = rdb.table("user").find(id).waitFor + check not res.get.hasKey("email") or res.get["email"].kind == JNull + + res = rdb.table("user").whereNull("email").first().waitFor + check not res.get.hasKey("email") or res.get["email"].kind == JNull + + + + test("insert [T]"): + type User = object + name:string + + let user = User(name: "Alice") + rdb.table("user").insert(user).waitFor + + let row = rdb.table("user").orderBy("index", Desc).first().waitFor().get() + check row["name"].getStr() == "Alice" + + + test("insert seq[T]"): + type User = object + name:string + + let users = @[ + User(name: "Alice"), + User(name: "Bob"), + ] + + rdb.table("user").insert(users).waitFor + let t = rdb.table("user").orderBy("index", Desc).limit(2).get().waitFor() + check t[0]["name"].getStr() == "Bob" + check t[1]["name"].getStr() == "Alice" + + +suite($rdb & " update"): + setup: + setup(rdb) + + + test("update"): + var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get() + let user1Id = SurrealId.new(user1["id"].getStr()) + rdb.table("user").where("id", "=", user1Id).update(%*{"name": "Alice"}).waitFor() + user1 = rdb.table("user").find(user1Id).waitFor().get() + check user1["name"].getStr() == "Alice" + + + test("update [T]"): + type User = object + name:string + + let user = User(name: "Alice") + var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get() + let user1Id = SurrealId.new(user1["id"].getStr()) + rdb.table("user").where("id", "=", user1Id).update(user).waitFor() + user1 = rdb.table("user").find(user1Id).waitFor().get() + check user1["name"].getStr() == "Alice" + + + test("update merge"): + var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get() + let user1Id = SurrealId.new(user1["id"].getStr()) + rdb.update(user1Id, %*{"name": "updated"}).waitFor() + user1 = rdb.table("user").find(user1Id).waitFor().get() + check user1["name"].getStr() == "updated" + + + test("update merge [T]"): + type User = object + name:string + + + var user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get() + let user1Id = SurrealId.new(user1["id"].getStr()) + let user = User(name: "updated") + rdb.update(user1Id, user).waitFor() + user1 = rdb.table("user").find(user1Id).waitFor().get() + check user1["name"].getStr() == "updated" + + +suite($rdb & " delete"): + setup: + setup(rdb) + + test("delete"): + let user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get() + let user1Id = SurrealId.new(user1["id"].getStr()) + rdb.table("user").where("name", "=", "user1").delete().waitFor() + check rdb.table("user").find(user1Id).waitFor().isSome() == false + + test("delete id"): + let user1 = rdb.table("user").where("index", "=", 1).first().waitFor().get() + let user1Id = SurrealId.new(user1["id"].getStr()) + rdb.table("user").delete(user1Id).waitFor + check rdb.table("user").find(user1Id).waitFor().isSome() == false + + +suite($rdb & " rawQuery"): + setup(rdb) + + test("get"): + let sql = &"SELECT * FROM `user` WHERE `index` = ?" + let res = rdb.raw(sql, %*[1]).get().waitFor + check res[0]["name"].getStr == "user1" + + + test("first"): + let sql = &"SELECT * FROM `user` WHERE `index` = ?" + let res = rdb.raw(sql, %*[1]).first().waitFor().get() + check res["name"].getStr() == "user1" + + + test("exec"): + var sql = "SELECT * FROM `user` WHERE `index` = ?" + let user1 = rdb.raw(sql, %*[1]).first().waitFor().get() + let user1Id = SurrealId.new(user1["id"].getStr()) + + sql = "UPDATE `user` SET `name` = ? WHERE `id` = ?" + rdb.raw(sql, %*["updated", user1Id.rawId()]).exec().waitFor + + sql = &"SELECT * FROM `user` WHERE `id` = ?" + let res = rdb.raw(sql, %[user1Id.rawId()]).get().waitFor + check res[0]["name"].getStr == "updated" + + +setup(rdb) +suite($rdb & " aggregates"): + + test("count"): + var t = rdb.table("user").count().waitFor() + check t == 10 + + test("max"): + var t = rdb.table("user").max("name", Collate).waitFor() + check t == "user9" + var t2 = rdb.table("user").max("index", Numeric).waitFor() + check t2 == "10" + + test("min"): + var t = rdb.table("user").min("name", Collate).waitFor() + check t == "user1" + var t2 = rdb.table("user").min("index", Numeric).waitFor() + check t2 == "1" + + test("avg"): + let t = rdb.table("user").avg("index").waitFor() + check t == 5.5 + + test("sum"): + let t = rdb.table("user").sum("index").waitFor() + check t == 55.0 + + +clearTables(rdb).waitFor() diff --git a/clients/nim-allographer/tests/surrealdb/test_schema.nim b/clients/nim-allographer/tests/surrealdb/test_schema.nim new file mode 100644 index 0000000..b1c7dd7 --- /dev/null +++ b/clients/nim-allographer/tests/surrealdb/test_schema.nim @@ -0,0 +1,532 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +# nim c -r -d:reset -d:ssl tests/surrealdb/test_schema.nim + +import std/unittest +import std/asyncdispatch +import std/json +import std/options +import std/strutils +import ../../src/allographer/schema_builder +import ../../src/allographer/query_builder +import ./connections +import ../clear_tables + + +proc normalizeInfoDef(s: string): string = + result = s + result = result.replace("`rand`::uuid()", "rand::uuid()") + result = result.replace("1.1f", "1.1") + result = result.replace("| none", "| NONE") + result = result.replace("| None", "| NONE") + result = result.replace(" PERMISSIONS FULL", "") + result = result.replace("{ key: 'value' }", "{\"key\":\"value\"}") + result = result.replace("{ key: \"value\" }", "{\"key\":\"value\"}") + result = result.replace("{key:'value'}", "{\"key\":\"value\"}") + + +proc `==`(a, b: string): bool = + cmp(normalizeInfoDef(a), normalizeInfoDef(b)) == 0 + + +proc expectInfoEntries(node: JsonNode, entries: seq[tuple[key: string, expected: string]]) = + for entry in entries: + check normalizeInfoDef(node[entry.key].getStr()) == normalizeInfoDef(entry.expected) + + +suite("SurrealDB create table"): + test("create table"): + surreal.create( + table("relation", [ + Column.uuid("uuid").unique(), + ]), + table("TypeIndex", [ + Column.increments("index"), + Column.integer("integer").unsigned().index().default(1), + Column.smallInteger("smallInteger").unsigned().index().default(1), + Column.mediumInteger("mediumInteger").unsigned().index().default(1), + Column.bigInteger("bigInteger").unsigned().index().default(1), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1), + Column.double("double", 10, 3).unsigned().index().default(1.1), + Column.float("float").unsigned().index().default(1.1), + Column.uuid("uuid").index().default("A"), + Column.char("char", 256).index().default("A"), + Column.string("string").index().default("A"), + Column.text("text").index().default("A"), + Column.mediumText("mediumText").index().default("A"), + Column.longText("longText").index().default("A"), + Column.date("date").index().default(), + Column.datetime("datetime1").index().default(Current), + Column.datetime("datetime2").index().default(CurrentOnUpdate), + Column.timestamp("timestamp").index().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").index().default("A"), + Column.boolean("boolean").index().default(true), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A"), + Column.json("json").index().default(%*{"key":"value"}), + Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable(), + ]), + table("TypeUnique", [ + Column.increments("index"), + Column.integer("integer").unsigned().index().unique().unique().default(1), + Column.smallInteger("smallInteger").unsigned().index().unique().unique().default(1), + Column.mediumInteger("mediumInteger").unsigned().index().unique().unique().default(1), + Column.bigInteger("bigInteger").unsigned().index().unique().unique().default(1), + Column.decimal("decimal", 10, 3).unsigned().index().unique().unique().default(1.1), + Column.double("double", 10, 3).unsigned().index().unique().unique().default(1.1), + Column.float("float").unsigned().index().unique().unique().default(1.1), + Column.uuid("uuid").index().unique().unique().default("A"), + Column.char("char", 256).index().unique().unique().default("A"), + Column.string("string").index().unique().unique().default("A"), + Column.text("text").index().unique().unique().default("A"), + Column.mediumText("mediumText").index().unique().unique().default("A"), + Column.longText("longText").index().unique().unique().default("A"), + Column.date("date").index().unique().unique().default(), + Column.datetime("datetime1").index().unique().unique().default(Current), + Column.datetime("datetime2").index().unique().unique().default(CurrentOnUpdate), + Column.timestamp("timestamp").index().unique().unique().default(), + Column.timestamps(), + Column.softDelete(), + Column.binary("binary").index().unique().unique().default("A"), + Column.boolean("boolean").index().unique().unique().default(true), + Column.enumField("enumField", ["A", "B", "C"]).index().unique().default("A"), + Column.json("json").index().unique().default(%*{"key":"value"}), + Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable(), + ]), + ) + + block: + let info = surreal.raw(""" INFO FOR TABLE TypeIndex """).info().waitFor() + let resultNode = info[0]["result"] + let fields = resultNode["fields"] + expectInfoEntries(fields, @[ + ("index", "DEFINE FIELD index ON TypeIndex TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeIndex' AND column = 'index' LIMIT 1)[0].max_index + 1"), + ("integer", "DEFINE FIELD integer ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("smallInteger", "DEFINE FIELD smallInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("mediumInteger", "DEFINE FIELD mediumInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("bigInteger", "DEFINE FIELD bigInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("decimal", "DEFINE FIELD decimal ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("double", "DEFINE FIELD double ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("float", "DEFINE FIELD float ON TypeIndex TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("uuid", "DEFINE FIELD uuid ON TypeIndex TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"), + ("char", "DEFINE FIELD char ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("string", "DEFINE FIELD string ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("text", "DEFINE FIELD text ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("mediumText", "DEFINE FIELD mediumText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("longText", "DEFINE FIELD longText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("date", "DEFINE FIELD date ON TypeIndex TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("datetime1", "DEFINE FIELD datetime1 ON TypeIndex TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"), + ("datetime2", "DEFINE FIELD datetime2 ON TypeIndex TYPE datetime VALUE time::now() ASSERT $value != NONE"), + ("timestamp", "DEFINE FIELD timestamp ON TypeIndex TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("created_at", "DEFINE FIELD created_at ON TypeIndex TYPE datetime VALUE $value OR time::now()"), + ("updated_at", "DEFINE FIELD updated_at ON TypeIndex TYPE datetime VALUE time::now()"), + ("deleted_at", "DEFINE FIELD deleted_at ON TypeIndex TYPE datetime"), + ("binary", "DEFINE FIELD binary ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("boolean", "DEFINE FIELD boolean ON TypeIndex TYPE bool VALUE $value OR true ASSERT $value != NONE"), + ("enumField", "DEFINE FIELD enumField ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"), + ("json", "DEFINE FIELD json ON TypeIndex TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"), + ("relation", "DEFINE FIELD relation ON TypeIndex TYPE record | NONE VALUE $value OR NONE"), + ]) + + let indexs = if resultNode.hasKey("indexes"): resultNode["indexes"] else: resultNode["ix"] + expectInfoEntries(indexs, @[ + ("TypeIndex_integer_index", "DEFINE INDEX TypeIndex_integer_index ON TypeIndex FIELDS integer"), + ("TypeIndex_smallInteger_index", "DEFINE INDEX TypeIndex_smallInteger_index ON TypeIndex FIELDS smallInteger"), + ("TypeIndex_mediumInteger_index", "DEFINE INDEX TypeIndex_mediumInteger_index ON TypeIndex FIELDS mediumInteger"), + ("TypeIndex_bigInteger_index", "DEFINE INDEX TypeIndex_bigInteger_index ON TypeIndex FIELDS bigInteger"), + ("TypeIndex_decimal_index", "DEFINE INDEX TypeIndex_decimal_index ON TypeIndex FIELDS decimal"), + ("TypeIndex_double_index", "DEFINE INDEX TypeIndex_double_index ON TypeIndex FIELDS double"), + ("TypeIndex_float_index", "DEFINE INDEX TypeIndex_float_index ON TypeIndex FIELDS float"), + ("TypeIndex_uuid_unique", "DEFINE INDEX TypeIndex_uuid_unique ON TypeIndex FIELDS uuid UNIQUE"), + ("TypeIndex_char_index", "DEFINE INDEX TypeIndex_char_index ON TypeIndex FIELDS char"), + ("TypeIndex_string_index", "DEFINE INDEX TypeIndex_string_index ON TypeIndex FIELDS string"), + ("TypeIndex_text_index", "DEFINE INDEX TypeIndex_text_index ON TypeIndex FIELDS text"), + ("TypeIndex_mediumText_index", "DEFINE INDEX TypeIndex_mediumText_index ON TypeIndex FIELDS mediumText"), + ("TypeIndex_longText_index", "DEFINE INDEX TypeIndex_longText_index ON TypeIndex FIELDS longText"), + ("TypeIndex_date_index", "DEFINE INDEX TypeIndex_date_index ON TypeIndex FIELDS date"), + ("TypeIndex_datetime1_index", "DEFINE INDEX TypeIndex_datetime1_index ON TypeIndex FIELDS datetime1"), + ("TypeIndex_datetime2_index", "DEFINE INDEX TypeIndex_datetime2_index ON TypeIndex FIELDS datetime2"), + ("TypeIndex_timestamp_index", "DEFINE INDEX TypeIndex_timestamp_index ON TypeIndex FIELDS timestamp"), + ("TypeIndex_created_at_index", "DEFINE INDEX TypeIndex_created_at_index ON TypeIndex FIELDS created_at"), + ("TypeIndex_updated_at_index", "DEFINE INDEX TypeIndex_updated_at_index ON TypeIndex FIELDS updated_at"), + ("TypeIndex_deleted_at_index", "DEFINE INDEX TypeIndex_deleted_at_index ON TypeIndex FIELDS deleted_at"), + ("TypeIndex_binary_index", "DEFINE INDEX TypeIndex_binary_index ON TypeIndex FIELDS binary"), + ("TypeIndex_boolean_index", "DEFINE INDEX TypeIndex_boolean_index ON TypeIndex FIELDS boolean"), + ("TypeIndex_enumField_index", "DEFINE INDEX TypeIndex_enumField_index ON TypeIndex FIELDS enumField"), + ("TypeIndex_json_index", "DEFINE INDEX TypeIndex_json_index ON TypeIndex FIELDS json"), + ]) + + block: + let info = surreal.raw(""" INFO FOR TABLE TypeUnique """).info().waitFor() + let resultNode = info[0]["result"] + let fields = resultNode["fields"] + expectInfoEntries(fields, @[ + ("index", "DEFINE FIELD index ON TypeUnique TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeUnique' AND column = 'index' LIMIT 1)[0].max_index + 1"), + ("integer", "DEFINE FIELD integer ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("smallInteger", "DEFINE FIELD smallInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("mediumInteger", "DEFINE FIELD mediumInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("bigInteger", "DEFINE FIELD bigInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("decimal", "DEFINE FIELD decimal ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("double", "DEFINE FIELD double ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("float", "DEFINE FIELD float ON TypeUnique TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("uuid", "DEFINE FIELD uuid ON TypeUnique TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"), + ("char", "DEFINE FIELD char ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("string", "DEFINE FIELD string ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("text", "DEFINE FIELD text ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("mediumText", "DEFINE FIELD mediumText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("longText", "DEFINE FIELD longText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("date", "DEFINE FIELD date ON TypeUnique TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("datetime1", "DEFINE FIELD datetime1 ON TypeUnique TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"), + ("datetime2", "DEFINE FIELD datetime2 ON TypeUnique TYPE datetime VALUE time::now() ASSERT $value != NONE"), + ("timestamp", "DEFINE FIELD timestamp ON TypeUnique TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("created_at", "DEFINE FIELD created_at ON TypeUnique TYPE datetime VALUE $value OR time::now()"), + ("updated_at", "DEFINE FIELD updated_at ON TypeUnique TYPE datetime VALUE time::now()"), + ("deleted_at", "DEFINE FIELD deleted_at ON TypeUnique TYPE datetime"), + ("binary", "DEFINE FIELD binary ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("boolean", "DEFINE FIELD boolean ON TypeUnique TYPE bool VALUE $value OR true ASSERT $value != NONE"), + ("enumField", "DEFINE FIELD enumField ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"), + ("json", "DEFINE FIELD json ON TypeUnique TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"), + ("relation", "DEFINE FIELD relation ON TypeUnique TYPE record | NONE VALUE $value OR NONE"), + ]) + + let indexs = resultNode["indexes"] + expectInfoEntries(indexs, @[ + ("TypeUnique_integer_unique", "DEFINE INDEX TypeUnique_integer_unique ON TypeUnique FIELDS integer UNIQUE"), + ("TypeUnique_smallInteger_unique", "DEFINE INDEX TypeUnique_smallInteger_unique ON TypeUnique FIELDS smallInteger UNIQUE"), + ("TypeUnique_mediumInteger_unique", "DEFINE INDEX TypeUnique_mediumInteger_unique ON TypeUnique FIELDS mediumInteger UNIQUE"), + ("TypeUnique_bigInteger_unique", "DEFINE INDEX TypeUnique_bigInteger_unique ON TypeUnique FIELDS bigInteger UNIQUE"), + ("TypeUnique_decimal_unique", "DEFINE INDEX TypeUnique_decimal_unique ON TypeUnique FIELDS decimal UNIQUE"), + ("TypeUnique_double_unique", "DEFINE INDEX TypeUnique_double_unique ON TypeUnique FIELDS double UNIQUE"), + ("TypeUnique_float_unique", "DEFINE INDEX TypeUnique_float_unique ON TypeUnique FIELDS float UNIQUE"), + ("TypeUnique_uuid_unique", "DEFINE INDEX TypeUnique_uuid_unique ON TypeUnique FIELDS uuid UNIQUE"), + ("TypeUnique_char_unique", "DEFINE INDEX TypeUnique_char_unique ON TypeUnique FIELDS char UNIQUE"), + ("TypeUnique_string_unique", "DEFINE INDEX TypeUnique_string_unique ON TypeUnique FIELDS string UNIQUE"), + ("TypeUnique_text_unique", "DEFINE INDEX TypeUnique_text_unique ON TypeUnique FIELDS text UNIQUE"), + ("TypeUnique_mediumText_unique", "DEFINE INDEX TypeUnique_mediumText_unique ON TypeUnique FIELDS mediumText UNIQUE"), + ("TypeUnique_longText_unique", "DEFINE INDEX TypeUnique_longText_unique ON TypeUnique FIELDS longText UNIQUE"), + ("TypeUnique_date_unique", "DEFINE INDEX TypeUnique_date_unique ON TypeUnique FIELDS date UNIQUE"), + ("TypeUnique_datetime1_unique", "DEFINE INDEX TypeUnique_datetime1_unique ON TypeUnique FIELDS datetime1 UNIQUE"), + ("TypeUnique_datetime2_unique", "DEFINE INDEX TypeUnique_datetime2_unique ON TypeUnique FIELDS datetime2 UNIQUE"), + ("TypeUnique_timestamp_unique", "DEFINE INDEX TypeUnique_timestamp_unique ON TypeUnique FIELDS timestamp UNIQUE"), + ("TypeUnique_created_at_index", "DEFINE INDEX TypeUnique_created_at_index ON TypeUnique FIELDS created_at"), + ("TypeUnique_updated_at_index", "DEFINE INDEX TypeUnique_updated_at_index ON TypeUnique FIELDS updated_at"), + ("TypeUnique_deleted_at_index", "DEFINE INDEX TypeUnique_deleted_at_index ON TypeUnique FIELDS deleted_at"), + ("TypeUnique_binary_unique", "DEFINE INDEX TypeUnique_binary_unique ON TypeUnique FIELDS binary UNIQUE"), + ("TypeUnique_boolean_unique", "DEFINE INDEX TypeUnique_boolean_unique ON TypeUnique FIELDS boolean UNIQUE"), + ("TypeUnique_enumField_unique", "DEFINE INDEX TypeUnique_enumField_unique ON TypeUnique FIELDS enumField UNIQUE"), + ("TypeUnique_json_unique", "DEFINE INDEX TypeUnique_json_unique ON TypeUnique FIELDS json UNIQUE"), + ]) + + + test("autoincrement"): + surreal.create( + table("test",[ + Column.increments("index"), + Column.integer("index2").autoIncrement(), + Column.string("string") + ]) + ) + + surreal.table("test").insert(%*{"string": "a"}).waitFor + surreal.table("test").insert(%*{"string": "b"}).waitFor + surreal.table("test").insert(%*{"string": "c"}).waitFor + surreal.table("test").where("string", "=", "b").delete().waitFor + surreal.table("test").insert(%*{"string": "d"}).waitFor + + let data = surreal.table("test").orderBy("index", Asc).get().waitFor + for row in data: + if row["string"].getStr == "a": + check row["index"].getInt == 1 + check row["index2"].getInt == 1 + + if row["string"].getStr == "c": + check row["index"].getInt == 3 + check row["index2"].getInt == 3 + + if row["string"].getStr == "d": + check row["index2"].getInt == 4 + + + suite("Datetime"): + test("datetime default"): + surreal.create( + table("test", [ + Column.string("name"), + Column.datetime("created_at").default(Current), + Column.datetime("updated_at").default(CurrentOnUpdate), + ]) + ) + + surreal.table("test").insert(%*{"name": "alice"}).waitFor() + + var alice = surreal.table("test").first().waitFor().get() + let aliceId = SurrealId.new(alice["id"].getStr()) + echo alice + let aliceCreatedAt1 = alice["created_at"].getStr() + let aliceUpdatedAt1 = alice["updated_at"].getStr() + + surreal.table("test").where("id", "=", aliceId).update(%*{"name": "updated"}).waitFor() + + alice = surreal.table("test").find(aliceId).waitFor().get() + echo alice + let aliceCreatedAt2 = alice["created_at"].getStr() + let aliceUpdatedAt2 = alice["updated_at"].getStr() + + check aliceCreatedAt1 == aliceCreatedAt2 + check aliceUpdatedAt1 != aliceUpdatedAt2 + + + test("timestamps"): + surreal.create( + table("test", [ + Column.string("name"), + Column.timestamps() + ]) + ) + + surreal.table("test").insert(%*{"name": "alice"}).waitFor() + + var alice = surreal.table("test").first().waitFor().get() + let aliceId = SurrealId.new(alice["id"].getStr()) + let aliceCreatedAt1 = alice["created_at"].getStr() + let aliceUpdatedAt1 = alice["updated_at"].getStr() + + surreal.table("test").where("id", "=", aliceId).update(%*{"name": "updated"}).waitFor() + + alice = surreal.table("test").find(aliceId).waitFor().get() + let aliceCreatedAt2 = alice["created_at"].getStr() + let aliceUpdatedAt2 = alice["updated_at"].getStr() + + check aliceCreatedAt1 == aliceCreatedAt2 + check aliceUpdatedAt1 != aliceUpdatedAt2 + + + +suite("SurrealDB alter table"): + setup: + surreal.create( + table("relation", [ + Column.uuid("uuid").unique(), + ]), + table("TypeUnique", [ + Column.integer("num"), + Column.string("str"), + ]), + table("TypeIndex", [ + Column.integer("num"), + Column.string("str"), + ]) + ) + + + test("add column"): + surreal.alter( + table("TypeIndex", [ + Column.increments("index").add(), + Column.integer("integer").unsigned().index().default(1).add(), + Column.smallInteger("smallInteger").unsigned().index().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().index().default(1).add(), + Column.bigInteger("bigInteger").unsigned().index().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().index().default(1.1).add(), + Column.double("double", 10, 3).unsigned().index().default(1.1).add(), + Column.float("float").unsigned().index().default(1.1).add(), + Column.uuid("uuid").index().default("A").add(), + Column.char("char", 256).index().default("A").add(), + Column.string("string").index().default("A").add(), + Column.text("text").index().default("A").add(), + Column.mediumText("mediumText").index().default("A").add(), + Column.longText("longText").index().default("A").add(), + Column.date("date").index().default().add(), + Column.datetime("datetime1").index().default(Current).add(), + Column.datetime("datetime2").index().default(CurrentOnUpdate).add(), + Column.timestamp("timestamp").index().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").index().default("A").add(), + Column.boolean("boolean").index().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).index().default("A").add(), + Column.json("json").index().default(%*{"key":"value"}).add(), + Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable().add(), + ]), + table("TypeUnique", [ + Column.increments("index").add(), + Column.integer("integer").unsigned().index().unique().unique().default(1).add(), + Column.smallInteger("smallInteger").unsigned().index().unique().unique().default(1).add(), + Column.mediumInteger("mediumInteger").unsigned().index().unique().unique().default(1).add(), + Column.bigInteger("bigInteger").unsigned().index().unique().unique().default(1).add(), + Column.decimal("decimal", 10, 3).unsigned().index().unique().unique().default(1.1).add(), + Column.double("double", 10, 3).unsigned().index().unique().unique().default(1.1).add(), + Column.float("float").unsigned().index().unique().unique().default(1.1).add(), + Column.uuid("uuid").index().unique().unique().default("A").add(), + Column.char("char", 256).index().unique().unique().default("A").add(), + Column.string("string").index().unique().unique().default("A").add(), + Column.text("text").index().unique().unique().default("A").add(), + Column.mediumText("mediumText").index().unique().unique().default("A").add(), + Column.longText("longText").index().unique().unique().default("A").add(), + Column.date("date").index().unique().unique().default().add(), + Column.datetime("datetime1").index().unique().unique().default(Current).add(), + Column.datetime("datetime2").index().unique().unique().default(CurrentOnUpdate).add(), + Column.timestamp("timestamp").index().unique().unique().default().add(), + Column.timestamps().add(), + Column.softDelete().add(), + Column.binary("binary").index().unique().unique().default("A").add(), + Column.boolean("boolean").index().unique().unique().default(true).add(), + Column.enumField("enumField", ["A", "B", "C"]).index().unique().default("A").add(), + Column.json("json").index().unique().default(%*{"key":"value"}).add(), + Column.foreign("relation").reference("id").onTable("relation").onDelete(SET_NULL).nullable().add(), + ]), + ) + + block: + let info = surreal.raw(""" INFO FOR TABLE TypeIndex """).info().waitFor() + let resultNode = info[0]["result"] + let fields = resultNode["fields"] + expectInfoEntries(fields, @[ + ("index", "DEFINE FIELD index ON TypeIndex TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeIndex' AND column = 'index' LIMIT 1)[0].max_index + 1"), + ("integer", "DEFINE FIELD integer ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("smallInteger", "DEFINE FIELD smallInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("mediumInteger", "DEFINE FIELD mediumInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("bigInteger", "DEFINE FIELD bigInteger ON TypeIndex TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("decimal", "DEFINE FIELD decimal ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("double", "DEFINE FIELD double ON TypeIndex TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("float", "DEFINE FIELD float ON TypeIndex TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("uuid", "DEFINE FIELD uuid ON TypeIndex TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"), + ("char", "DEFINE FIELD char ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("string", "DEFINE FIELD string ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("text", "DEFINE FIELD text ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("mediumText", "DEFINE FIELD mediumText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("longText", "DEFINE FIELD longText ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("date", "DEFINE FIELD date ON TypeIndex TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("datetime1", "DEFINE FIELD datetime1 ON TypeIndex TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"), + ("datetime2", "DEFINE FIELD datetime2 ON TypeIndex TYPE datetime VALUE time::now() ASSERT $value != NONE"), + ("timestamp", "DEFINE FIELD timestamp ON TypeIndex TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("created_at", "DEFINE FIELD created_at ON TypeIndex TYPE datetime VALUE $value OR time::now()"), + ("updated_at", "DEFINE FIELD updated_at ON TypeIndex TYPE datetime VALUE time::now()"), + ("deleted_at", "DEFINE FIELD deleted_at ON TypeIndex TYPE datetime"), + ("binary", "DEFINE FIELD binary ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("boolean", "DEFINE FIELD boolean ON TypeIndex TYPE bool VALUE $value OR true ASSERT $value != NONE"), + ("enumField", "DEFINE FIELD enumField ON TypeIndex TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"), + ("json", "DEFINE FIELD json ON TypeIndex TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"), + ("relation", "DEFINE FIELD relation ON TypeIndex TYPE record | NONE VALUE $value OR NONE"), + ]) + + let indexs = resultNode["indexes"] + expectInfoEntries(indexs, @[ + ("TypeIndex_integer_index", "DEFINE INDEX TypeIndex_integer_index ON TypeIndex FIELDS integer"), + ("TypeIndex_smallInteger_index", "DEFINE INDEX TypeIndex_smallInteger_index ON TypeIndex FIELDS smallInteger"), + ("TypeIndex_mediumInteger_index", "DEFINE INDEX TypeIndex_mediumInteger_index ON TypeIndex FIELDS mediumInteger"), + ("TypeIndex_bigInteger_index", "DEFINE INDEX TypeIndex_bigInteger_index ON TypeIndex FIELDS bigInteger"), + ("TypeIndex_decimal_index", "DEFINE INDEX TypeIndex_decimal_index ON TypeIndex FIELDS decimal"), + ("TypeIndex_double_index", "DEFINE INDEX TypeIndex_double_index ON TypeIndex FIELDS double"), + ("TypeIndex_float_index", "DEFINE INDEX TypeIndex_float_index ON TypeIndex FIELDS float"), + ("TypeIndex_uuid_unique", "DEFINE INDEX TypeIndex_uuid_unique ON TypeIndex FIELDS uuid UNIQUE"), + ("TypeIndex_char_index", "DEFINE INDEX TypeIndex_char_index ON TypeIndex FIELDS char"), + ("TypeIndex_string_index", "DEFINE INDEX TypeIndex_string_index ON TypeIndex FIELDS string"), + ("TypeIndex_text_index", "DEFINE INDEX TypeIndex_text_index ON TypeIndex FIELDS text"), + ("TypeIndex_mediumText_index", "DEFINE INDEX TypeIndex_mediumText_index ON TypeIndex FIELDS mediumText"), + ("TypeIndex_longText_index", "DEFINE INDEX TypeIndex_longText_index ON TypeIndex FIELDS longText"), + ("TypeIndex_date_index", "DEFINE INDEX TypeIndex_date_index ON TypeIndex FIELDS date"), + ("TypeIndex_datetime1_index", "DEFINE INDEX TypeIndex_datetime1_index ON TypeIndex FIELDS datetime1"), + ("TypeIndex_datetime2_index", "DEFINE INDEX TypeIndex_datetime2_index ON TypeIndex FIELDS datetime2"), + ("TypeIndex_timestamp_index", "DEFINE INDEX TypeIndex_timestamp_index ON TypeIndex FIELDS timestamp"), + ("TypeIndex_created_at_index", "DEFINE INDEX TypeIndex_created_at_index ON TypeIndex FIELDS created_at"), + ("TypeIndex_updated_at_index", "DEFINE INDEX TypeIndex_updated_at_index ON TypeIndex FIELDS updated_at"), + ("TypeIndex_deleted_at_index", "DEFINE INDEX TypeIndex_deleted_at_index ON TypeIndex FIELDS deleted_at"), + ("TypeIndex_binary_index", "DEFINE INDEX TypeIndex_binary_index ON TypeIndex FIELDS binary"), + ("TypeIndex_boolean_index", "DEFINE INDEX TypeIndex_boolean_index ON TypeIndex FIELDS boolean"), + ("TypeIndex_enumField_index", "DEFINE INDEX TypeIndex_enumField_index ON TypeIndex FIELDS enumField"), + ("TypeIndex_json_index", "DEFINE INDEX TypeIndex_json_index ON TypeIndex FIELDS json"), + ]) + + block: + let info = surreal.raw(""" INFO FOR TABLE TypeUnique """).info().waitFor() + let resultNode = info[0]["result"] + let fields = resultNode["fields"] + expectInfoEntries(fields, @[ + ("index", "DEFINE FIELD index ON TypeUnique TYPE int VALUE $value OR (SELECT max_index FROM _autoincrement_sequences WHERE `table` = 'TypeUnique' AND column = 'index' LIMIT 1)[0].max_index + 1"), + ("integer", "DEFINE FIELD integer ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("smallInteger", "DEFINE FIELD smallInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("mediumInteger", "DEFINE FIELD mediumInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("bigInteger", "DEFINE FIELD bigInteger ON TypeUnique TYPE int VALUE $value OR 1 ASSERT $value != NONE AND $value >= 0"), + ("decimal", "DEFINE FIELD decimal ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("double", "DEFINE FIELD double ON TypeUnique TYPE decimal VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("float", "DEFINE FIELD float ON TypeUnique TYPE float VALUE $value OR 1.1 ASSERT $value != NONE AND $value >= 0"), + ("uuid", "DEFINE FIELD uuid ON TypeUnique TYPE string VALUE $value OR rand::uuid() ASSERT $value != NONE"), + ("char", "DEFINE FIELD char ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("string", "DEFINE FIELD string ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT string::len($value) < 256 AND $value != NONE"), + ("text", "DEFINE FIELD text ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("mediumText", "DEFINE FIELD mediumText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("longText", "DEFINE FIELD longText ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("date", "DEFINE FIELD date ON TypeUnique TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("datetime1", "DEFINE FIELD datetime1 ON TypeUnique TYPE datetime VALUE $value OR time::now() ASSERT $value != NONE"), + ("datetime2", "DEFINE FIELD datetime2 ON TypeUnique TYPE datetime VALUE time::now() ASSERT $value != NONE"), + ("timestamp", "DEFINE FIELD timestamp ON TypeUnique TYPE datetime VALUE $value OR '1970-01-01T00:00:00Z' ASSERT $value != NONE"), + ("created_at", "DEFINE FIELD created_at ON TypeUnique TYPE datetime VALUE $value OR time::now()"), + ("updated_at", "DEFINE FIELD updated_at ON TypeUnique TYPE datetime VALUE time::now()"), + ("deleted_at", "DEFINE FIELD deleted_at ON TypeUnique TYPE datetime"), + ("binary", "DEFINE FIELD binary ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value != NONE"), + ("boolean", "DEFINE FIELD boolean ON TypeUnique TYPE bool VALUE $value OR true ASSERT $value != NONE"), + ("enumField", "DEFINE FIELD enumField ON TypeUnique TYPE string VALUE $value OR 'A' ASSERT $value INSIDE ['A', 'B', 'C'] AND $value != NONE"), + ("json", "DEFINE FIELD json ON TypeUnique TYPE object FLEXIBLE VALUE $value OR { key: 'value' } ASSERT $value != NONE"), + ("relation", "DEFINE FIELD relation ON TypeUnique TYPE record | NONE VALUE $value OR NONE"), + ]) + + let indexs = if resultNode.hasKey("indexes"): resultNode["indexes"] else: resultNode["ix"] + expectInfoEntries(indexs, @[ + ("TypeUnique_integer_unique", "DEFINE INDEX TypeUnique_integer_unique ON TypeUnique FIELDS integer UNIQUE"), + ("TypeUnique_smallInteger_unique", "DEFINE INDEX TypeUnique_smallInteger_unique ON TypeUnique FIELDS smallInteger UNIQUE"), + ("TypeUnique_mediumInteger_unique", "DEFINE INDEX TypeUnique_mediumInteger_unique ON TypeUnique FIELDS mediumInteger UNIQUE"), + ("TypeUnique_bigInteger_unique", "DEFINE INDEX TypeUnique_bigInteger_unique ON TypeUnique FIELDS bigInteger UNIQUE"), + ("TypeUnique_decimal_unique", "DEFINE INDEX TypeUnique_decimal_unique ON TypeUnique FIELDS decimal UNIQUE"), + ("TypeUnique_double_unique", "DEFINE INDEX TypeUnique_double_unique ON TypeUnique FIELDS double UNIQUE"), + ("TypeUnique_float_unique", "DEFINE INDEX TypeUnique_float_unique ON TypeUnique FIELDS float UNIQUE"), + ("TypeUnique_uuid_unique", "DEFINE INDEX TypeUnique_uuid_unique ON TypeUnique FIELDS uuid UNIQUE"), + ("TypeUnique_char_unique", "DEFINE INDEX TypeUnique_char_unique ON TypeUnique FIELDS char UNIQUE"), + ("TypeUnique_string_unique", "DEFINE INDEX TypeUnique_string_unique ON TypeUnique FIELDS string UNIQUE"), + ("TypeUnique_text_unique", "DEFINE INDEX TypeUnique_text_unique ON TypeUnique FIELDS text UNIQUE"), + ("TypeUnique_mediumText_unique", "DEFINE INDEX TypeUnique_mediumText_unique ON TypeUnique FIELDS mediumText UNIQUE"), + ("TypeUnique_longText_unique", "DEFINE INDEX TypeUnique_longText_unique ON TypeUnique FIELDS longText UNIQUE"), + ("TypeUnique_date_unique", "DEFINE INDEX TypeUnique_date_unique ON TypeUnique FIELDS date UNIQUE"), + ("TypeUnique_datetime1_unique", "DEFINE INDEX TypeUnique_datetime1_unique ON TypeUnique FIELDS datetime1 UNIQUE"), + ("TypeUnique_datetime2_unique", "DEFINE INDEX TypeUnique_datetime2_unique ON TypeUnique FIELDS datetime2 UNIQUE"), + ("TypeUnique_timestamp_unique", "DEFINE INDEX TypeUnique_timestamp_unique ON TypeUnique FIELDS timestamp UNIQUE"), + ("TypeUnique_created_at_index", "DEFINE INDEX TypeUnique_created_at_index ON TypeUnique FIELDS created_at"), + ("TypeUnique_updated_at_index", "DEFINE INDEX TypeUnique_updated_at_index ON TypeUnique FIELDS updated_at"), + ("TypeUnique_deleted_at_index", "DEFINE INDEX TypeUnique_deleted_at_index ON TypeUnique FIELDS deleted_at"), + ("TypeUnique_binary_unique", "DEFINE INDEX TypeUnique_binary_unique ON TypeUnique FIELDS binary UNIQUE"), + ("TypeUnique_boolean_unique", "DEFINE INDEX TypeUnique_boolean_unique ON TypeUnique FIELDS boolean UNIQUE"), + ("TypeUnique_enumField_unique", "DEFINE INDEX TypeUnique_enumField_unique ON TypeUnique FIELDS enumField UNIQUE"), + ("TypeUnique_json_unique", "DEFINE INDEX TypeUnique_json_unique ON TypeUnique FIELDS json UNIQUE"), + ]) + + + test("drop column"): + surreal.alter( + table("TypeIndex", [ + Column.dropColumn("str") + ]) + ) + + let columns = surreal.table("TypeIndex").columns().waitFor + check not columns.contains("str") + + + test("drop table"): + surreal.create( + table("TypeIndex", [ + Column.integer("num") + ]) + ) + + surreal.drop( + table("TypeIndex") + ) + + let res = surreal.table("TypeIndex").first().waitFor() + check not res.isSome + + +clearTables(surreal).waitFor() diff --git a/clients/nim-allographer/tests/utils/test_database_url.nim b/clients/nim-allographer/tests/utils/test_database_url.nim new file mode 100644 index 0000000..c3ef217 --- /dev/null +++ b/clients/nim-allographer/tests/utils/test_database_url.nim @@ -0,0 +1,42 @@ +discard """ + cmd: "nim c -d:reset $file" +""" + +import std/unittest +import ../../src/allographer/query_builder/libs/database_url + + +suite("database url"): + test("parse query and credentials"): + let url = parseDatabaseUrl( + "postgresql://user%40mail:pa%3Ass@host:5432/db%2Fname?sslmode=require&application_name=allographer" + ) + + check url.scheme == "postgresql" + check url.username == "user@mail" + check url.password == "pa:ss" + check url.hostname == "host" + check url.hasPort + check url.port == 5432 + check url.databaseName == "db/name" + check url.query == @[ + (key: "sslmode", value: "require"), + (key: "application_name", value: "allographer"), + ] + + + test("postgres alias stays valid"): + let url = parseDatabaseUrl("postgres://user:pass@host:5432/database") + check url.scheme == "postgres" + + + test("port defaults are explicit"): + let url = parseDatabaseUrl("mysql://user:pass@host/database") + check not url.hasPort + check portOrDefault(url, 3306) == 3306 + + + test("sqlite path from url"): + let url = parseDatabaseUrl("sqlite://./relative/path.db") + check sqliteDatabasePath(url) == "./relative/path.db" + check sqliteDatabasePath(parseDatabaseUrl("sqlite://relative/path.db")) == "relative/path.db" diff --git a/clients/nim-allographer/tests/utils/test_utils.nim b/clients/nim-allographer/tests/utils/test_utils.nim new file mode 100644 index 0000000..73e7e37 --- /dev/null +++ b/clients/nim-allographer/tests/utils/test_utils.nim @@ -0,0 +1,16 @@ +discard """ + cmd: "nim c -d:reset $file" +""" +# nim c -r tests/utils/test_utils.nim + + +import std/unittest +import ../../src/allographer/utils/snake_to_camel + + +suite("test utils"): + test("snakeToCamel"): + check snakeToCamel("test") == "Test" + check snakeToCamel("test_case") == "TestCase" + check snakeToCamel("test_case_abc") == "TestCaseAbc" + check snakeToCamel("test_case_1") == "TestCase1"