@neverinfamous/mysql-mcp 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +1 -0
- package/.gitattributes +18 -0
- package/.github/workflows/codeql.yml +2 -10
- package/.github/workflows/docker-publish.yml +15 -13
- package/CHANGELOG.md +287 -1
- package/DOCKER_README.md +100 -265
- package/Dockerfile +5 -0
- package/README.md +124 -59
- package/VERSION +1 -1
- package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
- package/dist/__tests__/mocks/adapter.js +2 -0
- package/dist/__tests__/mocks/adapter.js.map +1 -1
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
- package/dist/adapters/DatabaseAdapter.js +50 -9
- package/dist/adapters/DatabaseAdapter.js.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.d.ts +6 -0
- package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.js +8 -0
- package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
- package/dist/adapters/mysql/SchemaManager.js +16 -15
- package/dist/adapters/mysql/SchemaManager.js.map +1 -1
- package/dist/adapters/mysql/prompts/index.js +10 -20
- package/dist/adapters/mysql/prompts/index.js.map +1 -1
- package/dist/adapters/mysql/prompts/proxysqlSetup.js +1 -1
- package/dist/adapters/mysql/resources/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/docstore.js +10 -7
- package/dist/adapters/mysql/resources/docstore.js.map +1 -1
- package/dist/adapters/mysql/resources/events.js +11 -8
- package/dist/adapters/mysql/resources/events.js.map +1 -1
- package/dist/adapters/mysql/resources/indexes.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/indexes.js +12 -15
- package/dist/adapters/mysql/resources/indexes.js.map +1 -1
- package/dist/adapters/mysql/resources/innodb.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/innodb.js +20 -17
- package/dist/adapters/mysql/resources/innodb.js.map +1 -1
- package/dist/adapters/mysql/resources/locks.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/locks.js +9 -6
- package/dist/adapters/mysql/resources/locks.js.map +1 -1
- package/dist/adapters/mysql/resources/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/performance.js +15 -15
- package/dist/adapters/mysql/resources/performance.js.map +1 -1
- package/dist/adapters/mysql/resources/spatial.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/spatial.js +9 -6
- package/dist/adapters/mysql/resources/spatial.js.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.js +12 -9
- package/dist/adapters/mysql/resources/sysschema.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.js +170 -121
- package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.js +106 -57
- package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.js +183 -101
- package/dist/adapters/mysql/tools/admin/monitoring.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.js +164 -120
- package/dist/adapters/mysql/tools/cluster/group-replication.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +212 -145
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.js +6 -4
- package/dist/adapters/mysql/tools/codemode/index.js.map +1 -1
- package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/core.js +152 -29
- package/dist/adapters/mysql/tools/core.js.map +1 -1
- package/dist/adapters/mysql/tools/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/docstore.js +340 -163
- package/dist/adapters/mysql/tools/docstore.js.map +1 -1
- package/dist/adapters/mysql/tools/events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/events.js +284 -198
- package/dist/adapters/mysql/tools/events.js.map +1 -1
- package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/core.js +11 -39
- package/dist/adapters/mysql/tools/json/core.js.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.js +15 -33
- package/dist/adapters/mysql/tools/json/enhanced.js.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.js +13 -24
- package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.js +3 -0
- package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.js +89 -60
- package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.js +151 -127
- package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.js +289 -176
- package/dist/adapters/mysql/tools/proxysql.js.map +1 -1
- package/dist/adapters/mysql/tools/replication.js +75 -49
- package/dist/adapters/mysql/tools/replication.js.map +1 -1
- package/dist/adapters/mysql/tools/roles.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/roles.js +224 -182
- package/dist/adapters/mysql/tools/roles.js.map +1 -1
- package/dist/adapters/mysql/tools/router.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/router.js +168 -67
- package/dist/adapters/mysql/tools/router.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.js +21 -3
- package/dist/adapters/mysql/tools/schema/constraints.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.js +61 -14
- package/dist/adapters/mysql/tools/schema/management.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.js +27 -4
- package/dist/adapters/mysql/tools/schema/routines.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.js +24 -3
- package/dist/adapters/mysql/tools/schema/scheduled_events.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.js +23 -2
- package/dist/adapters/mysql/tools/schema/triggers.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.js +47 -7
- package/dist/adapters/mysql/tools/schema/views.js.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.js +102 -34
- package/dist/adapters/mysql/tools/security/audit.js.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.js +264 -205
- package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.js +137 -104
- package/dist/adapters/mysql/tools/security/encryption.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.js +71 -59
- package/dist/adapters/mysql/tools/shell/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.js +61 -47
- package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.js +19 -5
- package/dist/adapters/mysql/tools/spatial/geometry.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.js +42 -17
- package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.js +109 -57
- package/dist/adapters/mysql/tools/spatial/queries.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.js +103 -50
- package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.js +128 -79
- package/dist/adapters/mysql/tools/stats/comparative.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.js +174 -102
- package/dist/adapters/mysql/tools/stats/descriptive.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.js +50 -25
- package/dist/adapters/mysql/tools/sysschema/activity.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.js +121 -66
- package/dist/adapters/mysql/tools/sysschema/performance.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.js +101 -64
- package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.js +18 -32
- package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
- package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/transactions.js +48 -23
- package/dist/adapters/mysql/tools/transactions.js.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.d.ts +15 -0
- package/dist/adapters/mysql/types/proxysql-types.d.ts.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.js +33 -1
- package/dist/adapters/mysql/types/proxysql-types.js.map +1 -1
- package/dist/adapters/mysql/types/router-types.d.ts +1 -1
- package/dist/adapters/mysql/types/router-types.js +1 -1
- package/dist/adapters/mysql/types/router-types.js.map +1 -1
- package/dist/adapters/mysql/types/shell-types.js +2 -2
- package/dist/adapters/mysql/types/shell-types.js.map +1 -1
- package/dist/adapters/mysql/types.d.ts +485 -21
- package/dist/adapters/mysql/types.d.ts.map +1 -1
- package/dist/adapters/mysql/types.js +546 -19
- package/dist/adapters/mysql/types.js.map +1 -1
- package/dist/auth/scopes.js +1 -1
- package/dist/auth/scopes.js.map +1 -1
- package/dist/codemode/api.d.ts +3 -2
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/api.js +80 -5
- package/dist/codemode/api.js.map +1 -1
- package/dist/codemode/sandbox-factory.js +1 -1
- package/dist/codemode/sandbox-factory.js.map +1 -1
- package/dist/codemode/types.d.ts +26 -0
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/codemode/types.js +2 -0
- package/dist/codemode/types.js.map +1 -1
- package/dist/codemode/worker-sandbox.d.ts +4 -2
- package/dist/codemode/worker-sandbox.d.ts.map +1 -1
- package/dist/codemode/worker-sandbox.js +66 -7
- package/dist/codemode/worker-sandbox.js.map +1 -1
- package/dist/codemode/worker-script.d.ts +3 -0
- package/dist/codemode/worker-script.d.ts.map +1 -1
- package/dist/codemode/worker-script.js +128 -75
- package/dist/codemode/worker-script.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +37 -31
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +1 -1
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +1 -2
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/pool/ConnectionPool.d.ts.map +1 -1
- package/dist/pool/ConnectionPool.js.map +1 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +6 -0
- package/dist/transports/http.js.map +1 -1
- package/dist/utils/validators.d.ts +1 -1
- package/dist/utils/validators.d.ts.map +1 -1
- package/dist/utils/validators.js.map +1 -1
- package/package.json +4 -4
- package/releases/v2.3.0-release-notes.md +20 -20
- package/releases/v2.3.1-release-notes.md +34 -0
- package/releases/v3.0.0-release-notes.md +81 -0
- package/src/__tests__/mocks/adapter.ts +3 -0
- package/src/__tests__/perf.test.ts +6 -6
- package/src/adapters/DatabaseAdapter.ts +58 -9
- package/src/adapters/__tests__/DatabaseAdapter.test.ts +89 -8
- package/src/adapters/mysql/MySQLAdapter.ts +17 -2
- package/src/adapters/mysql/SchemaManager.ts +21 -21
- package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
- package/src/adapters/mysql/prompts/index.ts +12 -22
- package/src/adapters/mysql/prompts/proxysqlSetup.ts +1 -1
- package/src/adapters/mysql/resources/docstore.ts +13 -10
- package/src/adapters/mysql/resources/events.ts +12 -12
- package/src/adapters/mysql/resources/indexes.ts +17 -19
- package/src/adapters/mysql/resources/innodb.ts +23 -22
- package/src/adapters/mysql/resources/locks.ts +9 -7
- package/src/adapters/mysql/resources/performance.ts +23 -18
- package/src/adapters/mysql/resources/spatial.ts +9 -7
- package/src/adapters/mysql/resources/sysschema.ts +12 -11
- package/src/adapters/mysql/tools/__tests__/core.test.ts +126 -55
- package/src/adapters/mysql/tools/__tests__/docstore.test.ts +459 -88
- package/src/adapters/mysql/tools/__tests__/events.test.ts +281 -103
- package/src/adapters/mysql/tools/__tests__/proxysql.test.ts +128 -28
- package/src/adapters/mysql/tools/__tests__/replication.test.ts +48 -2
- package/src/adapters/mysql/tools/__tests__/roles.test.ts +15 -18
- package/src/adapters/mysql/tools/__tests__/router.test.ts +32 -5
- package/src/adapters/mysql/tools/__tests__/security.test.ts +126 -2
- package/src/adapters/mysql/tools/__tests__/security_injection.test.ts +84 -76
- package/src/adapters/mysql/tools/__tests__/security_integration.test.ts +47 -50
- package/src/adapters/mysql/tools/__tests__/spatial.test.ts +11 -10
- package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +54 -38
- package/src/adapters/mysql/tools/__tests__/stats.test.ts +285 -152
- package/src/adapters/mysql/tools/__tests__/transactions.test.ts +13 -13
- package/src/adapters/mysql/tools/admin/__tests__/backup.test.ts +171 -25
- package/src/adapters/mysql/tools/admin/__tests__/maintenance.test.ts +240 -4
- package/src/adapters/mysql/tools/admin/__tests__/monitoring-summary.test.ts +274 -0
- package/src/adapters/mysql/tools/admin/__tests__/monitoring.test.ts +94 -5
- package/src/adapters/mysql/tools/admin/backup.ts +193 -143
- package/src/adapters/mysql/tools/admin/maintenance.ts +118 -69
- package/src/adapters/mysql/tools/admin/monitoring.ts +201 -125
- package/src/adapters/mysql/tools/cluster/__tests__/group-replication.test.ts +69 -0
- package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +141 -0
- package/src/adapters/mysql/tools/cluster/group-replication.ts +172 -132
- package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +231 -157
- package/src/adapters/mysql/tools/codemode/__tests__/codemode-tool.test.ts +227 -0
- package/src/adapters/mysql/tools/codemode/index.ts +5 -3
- package/src/adapters/mysql/tools/core.ts +152 -38
- package/src/adapters/mysql/tools/docstore.ts +422 -205
- package/src/adapters/mysql/tools/events.ts +334 -233
- package/src/adapters/mysql/tools/json/__tests__/core.test.ts +20 -0
- package/src/adapters/mysql/tools/json/__tests__/enhanced.test.ts +82 -50
- package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +42 -3
- package/src/adapters/mysql/tools/json/core.ts +21 -42
- package/src/adapters/mysql/tools/json/enhanced.ts +22 -37
- package/src/adapters/mysql/tools/json/helpers.ts +21 -25
- package/src/adapters/mysql/tools/partitioning.ts +3 -0
- package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +98 -5
- package/src/adapters/mysql/tools/performance/__tests__/optimization-coverage.test.ts +515 -0
- package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +187 -0
- package/src/adapters/mysql/tools/performance/analysis.ts +95 -69
- package/src/adapters/mysql/tools/performance/optimization.ts +182 -153
- package/src/adapters/mysql/tools/proxysql.ts +314 -209
- package/src/adapters/mysql/tools/replication.ts +84 -57
- package/src/adapters/mysql/tools/roles.ts +274 -226
- package/src/adapters/mysql/tools/router.ts +181 -85
- package/src/adapters/mysql/tools/schema/__tests__/constraints.test.ts +13 -0
- package/src/adapters/mysql/tools/schema/__tests__/management.test.ts +60 -25
- package/src/adapters/mysql/tools/schema/__tests__/scheduled_events.test.ts +11 -0
- package/src/adapters/mysql/tools/schema/__tests__/triggers.test.ts +25 -4
- package/src/adapters/mysql/tools/schema/__tests__/views.test.ts +46 -14
- package/src/adapters/mysql/tools/schema/constraints.ts +22 -3
- package/src/adapters/mysql/tools/schema/management.ts +60 -15
- package/src/adapters/mysql/tools/schema/routines.ts +26 -4
- package/src/adapters/mysql/tools/schema/scheduled_events.ts +25 -3
- package/src/adapters/mysql/tools/schema/triggers.ts +27 -2
- package/src/adapters/mysql/tools/schema/views.ts +46 -8
- package/src/adapters/mysql/tools/security/__tests__/audit.test.ts +90 -4
- package/src/adapters/mysql/tools/security/audit.ts +113 -39
- package/src/adapters/mysql/tools/security/data-protection.ts +293 -233
- package/src/adapters/mysql/tools/security/encryption.ts +172 -139
- package/src/adapters/mysql/tools/shell/__tests__/backup.test.ts +29 -0
- package/src/adapters/mysql/tools/shell/backup.ts +90 -73
- package/src/adapters/mysql/tools/shell/restore.ts +62 -48
- package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +22 -14
- package/src/adapters/mysql/tools/spatial/__tests__/queries.test.ts +65 -51
- package/src/adapters/mysql/tools/spatial/geometry.ts +23 -7
- package/src/adapters/mysql/tools/spatial/operations.ts +60 -31
- package/src/adapters/mysql/tools/spatial/queries.ts +142 -65
- package/src/adapters/mysql/tools/spatial/setup.ts +121 -55
- package/src/adapters/mysql/tools/stats/__tests__/comparative.test.ts +12 -10
- package/src/adapters/mysql/tools/stats/comparative.ts +150 -98
- package/src/adapters/mysql/tools/stats/descriptive.ts +204 -127
- package/src/adapters/mysql/tools/sysschema/__tests__/error-paths.test.ts +222 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/performance.test.ts +45 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +6 -3
- package/src/adapters/mysql/tools/sysschema/activity.ts +52 -27
- package/src/adapters/mysql/tools/sysschema/performance.ts +132 -68
- package/src/adapters/mysql/tools/sysschema/resources.ts +105 -67
- package/src/adapters/mysql/tools/text/__tests__/fulltext.test.ts +45 -17
- package/src/adapters/mysql/tools/text/fulltext.ts +27 -38
- package/src/adapters/mysql/tools/transactions.ts +49 -24
- package/src/adapters/mysql/types/proxysql-types.ts +38 -1
- package/src/adapters/mysql/types/router-types.ts +1 -1
- package/src/adapters/mysql/types/shell-types.ts +2 -2
- package/src/adapters/mysql/types.ts +632 -19
- package/src/auth/__tests__/scopes.test.ts +2 -2
- package/src/auth/scopes.ts +1 -1
- package/src/codemode/__tests__/api.test.ts +417 -0
- package/src/codemode/__tests__/sandbox-factory.test.ts +158 -0
- package/src/codemode/__tests__/sandbox.test.ts +301 -0
- package/src/codemode/__tests__/security.test.ts +368 -0
- package/src/codemode/__tests__/worker-sandbox.test.ts +179 -0
- package/src/codemode/__tests__/worker-script.test.ts +226 -0
- package/src/codemode/api.ts +89 -5
- package/src/codemode/sandbox-factory.ts +1 -1
- package/src/codemode/types.ts +34 -0
- package/src/codemode/worker-sandbox.ts +74 -7
- package/src/codemode/worker-script.ts +157 -86
- package/src/constants/ServerInstructions.ts +37 -31
- package/src/filtering/ToolConstants.ts +1 -2
- package/src/filtering/__tests__/ToolFilter.test.ts +9 -9
- package/src/pool/ConnectionPool.ts +4 -1
- package/src/transports/__tests__/http.test.ts +15 -3
- package/src/transports/http.ts +12 -0
- package/src/utils/validators.ts +2 -1
- package/vitest.config.ts +3 -1
- package/CODE_MODE.md +0 -245
|
@@ -5,12 +5,45 @@
|
|
|
5
5
|
* 4 tools: distance, distance_sphere, contains, within.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { z } from "zod";
|
|
8
|
+
import { z, ZodError } from "zod";
|
|
9
9
|
import type { MySQLAdapter } from "../../MySQLAdapter.js";
|
|
10
10
|
import type {
|
|
11
11
|
ToolDefinition,
|
|
12
12
|
RequestContext,
|
|
13
13
|
} from "../../../../types/index.js";
|
|
14
|
+
import {
|
|
15
|
+
validateQualifiedIdentifier,
|
|
16
|
+
escapeQualifiedTable,
|
|
17
|
+
} from "../../../../utils/validators.js";
|
|
18
|
+
import { ValidationError } from "../../../../utils/validators.js";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Helpers
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/** Extract human-readable messages from a ZodError instead of raw JSON array */
|
|
25
|
+
function formatZodError(error: ZodError): string {
|
|
26
|
+
return error.issues.map((i) => i.message).join("; ");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Strip verbose adapter prefixes from MySQL error messages */
|
|
30
|
+
function stripErrorPrefix(msg: string): string {
|
|
31
|
+
return msg.replace(/^(Query failed:\s*)?(Execute failed:\s*)?/i, "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Safely extract a string field from raw params for error context */
|
|
35
|
+
function paramStr(params: unknown, key: string): string {
|
|
36
|
+
if (
|
|
37
|
+
params !== null &&
|
|
38
|
+
params !== undefined &&
|
|
39
|
+
typeof params === "object" &&
|
|
40
|
+
key in params
|
|
41
|
+
) {
|
|
42
|
+
const val = (params as Record<string, unknown>)[key];
|
|
43
|
+
return typeof val === "string" ? val : "";
|
|
44
|
+
}
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
14
47
|
|
|
15
48
|
// =============================================================================
|
|
16
49
|
// Zod Schemas
|
|
@@ -71,25 +104,24 @@ export function createSpatialDistanceTool(
|
|
|
71
104
|
idempotentHint: true,
|
|
72
105
|
},
|
|
73
106
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
try {
|
|
108
|
+
const { table, spatialColumn, point, maxDistance, limit, srid } =
|
|
109
|
+
DistanceSchema.parse(params);
|
|
76
110
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
throw new Error("Invalid column name");
|
|
83
|
-
}
|
|
111
|
+
// Validate identifiers
|
|
112
|
+
validateQualifiedIdentifier(table, "table");
|
|
113
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(spatialColumn)) {
|
|
114
|
+
return { success: false, error: "Invalid column name" };
|
|
115
|
+
}
|
|
84
116
|
|
|
85
|
-
try {
|
|
86
117
|
// Use 'axis-order=long-lat' to accept natural longitude-latitude order
|
|
87
118
|
const pointWkt = `POINT(${String(point.longitude)} ${String(point.latitude)})`;
|
|
119
|
+
const escapedTable = escapeQualifiedTable(table);
|
|
88
120
|
|
|
89
121
|
let query = `
|
|
90
|
-
SELECT *,
|
|
122
|
+
SELECT *, ST_AsText(\`${spatialColumn}\`) as ${spatialColumn}_wkt,
|
|
91
123
|
ST_Distance(\`${spatialColumn}\`, ST_GeomFromText(?, ${String(srid)}, 'axis-order=long-lat')) as distance
|
|
92
|
-
FROM
|
|
124
|
+
FROM ${escapedTable}
|
|
93
125
|
`;
|
|
94
126
|
|
|
95
127
|
const queryParams: unknown[] = [pointWkt];
|
|
@@ -102,17 +134,29 @@ export function createSpatialDistanceTool(
|
|
|
102
134
|
query += ` ORDER BY distance LIMIT ${String(limit)}`;
|
|
103
135
|
|
|
104
136
|
const result = await adapter.executeQuery(query, queryParams);
|
|
137
|
+
// Strip raw binary spatial column from each row
|
|
138
|
+
const rows = (result.rows ?? []).map((row: Record<string, unknown>) =>
|
|
139
|
+
Object.fromEntries(
|
|
140
|
+
Object.entries(row).filter(([key]) => key !== spatialColumn),
|
|
141
|
+
),
|
|
142
|
+
);
|
|
105
143
|
return {
|
|
106
|
-
results:
|
|
107
|
-
count:
|
|
144
|
+
results: rows,
|
|
145
|
+
count: rows.length,
|
|
108
146
|
referencePoint: point,
|
|
109
147
|
};
|
|
110
148
|
} catch (error) {
|
|
149
|
+
if (error instanceof ZodError) {
|
|
150
|
+
return { success: false, error: formatZodError(error) };
|
|
151
|
+
}
|
|
152
|
+
if (error instanceof ValidationError) {
|
|
153
|
+
return { success: false, error: error.message };
|
|
154
|
+
}
|
|
111
155
|
const msg = error instanceof Error ? error.message : String(error);
|
|
112
156
|
if (msg.includes("doesn't exist")) {
|
|
113
|
-
return { exists: false, table };
|
|
157
|
+
return { exists: false, table: paramStr(params, "table") };
|
|
114
158
|
}
|
|
115
|
-
return { success: false, error: msg };
|
|
159
|
+
return { success: false, error: stripErrorPrefix(msg) };
|
|
116
160
|
}
|
|
117
161
|
},
|
|
118
162
|
};
|
|
@@ -137,25 +181,24 @@ export function createSpatialDistanceSphereTool(
|
|
|
137
181
|
idempotentHint: true,
|
|
138
182
|
},
|
|
139
183
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
140
|
-
|
|
141
|
-
|
|
184
|
+
try {
|
|
185
|
+
const { table, spatialColumn, point, maxDistance, limit, srid } =
|
|
186
|
+
DistanceSchema.parse(params);
|
|
142
187
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
throw new Error("Invalid column name");
|
|
149
|
-
}
|
|
188
|
+
// Validate identifiers
|
|
189
|
+
validateQualifiedIdentifier(table, "table");
|
|
190
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(spatialColumn)) {
|
|
191
|
+
return { success: false, error: "Invalid column name" };
|
|
192
|
+
}
|
|
150
193
|
|
|
151
|
-
try {
|
|
152
194
|
// Use 'axis-order=long-lat' to accept natural longitude-latitude order
|
|
153
195
|
const pointWkt = `POINT(${String(point.longitude)} ${String(point.latitude)})`;
|
|
196
|
+
const escapedTable = escapeQualifiedTable(table);
|
|
154
197
|
|
|
155
198
|
let query = `
|
|
156
|
-
SELECT *,
|
|
199
|
+
SELECT *, ST_AsText(\`${spatialColumn}\`) as ${spatialColumn}_wkt,
|
|
157
200
|
ST_Distance_Sphere(\`${spatialColumn}\`, ST_GeomFromText(?, ${String(srid)}, 'axis-order=long-lat')) as distance_meters
|
|
158
|
-
FROM
|
|
201
|
+
FROM ${escapedTable}
|
|
159
202
|
`;
|
|
160
203
|
|
|
161
204
|
const queryParams: unknown[] = [pointWkt];
|
|
@@ -168,18 +211,30 @@ export function createSpatialDistanceSphereTool(
|
|
|
168
211
|
query += ` ORDER BY distance_meters LIMIT ${String(limit)}`;
|
|
169
212
|
|
|
170
213
|
const result = await adapter.executeQuery(query, queryParams);
|
|
214
|
+
// Strip raw binary spatial column from each row
|
|
215
|
+
const rows = (result.rows ?? []).map((row: Record<string, unknown>) =>
|
|
216
|
+
Object.fromEntries(
|
|
217
|
+
Object.entries(row).filter(([key]) => key !== spatialColumn),
|
|
218
|
+
),
|
|
219
|
+
);
|
|
171
220
|
return {
|
|
172
|
-
results:
|
|
173
|
-
count:
|
|
221
|
+
results: rows,
|
|
222
|
+
count: rows.length,
|
|
174
223
|
referencePoint: point,
|
|
175
224
|
unit: "meters",
|
|
176
225
|
};
|
|
177
226
|
} catch (error) {
|
|
227
|
+
if (error instanceof ZodError) {
|
|
228
|
+
return { success: false, error: formatZodError(error) };
|
|
229
|
+
}
|
|
230
|
+
if (error instanceof ValidationError) {
|
|
231
|
+
return { success: false, error: error.message };
|
|
232
|
+
}
|
|
178
233
|
const msg = error instanceof Error ? error.message : String(error);
|
|
179
234
|
if (msg.includes("doesn't exist")) {
|
|
180
|
-
return { exists: false, table };
|
|
235
|
+
return { exists: false, table: paramStr(params, "table") };
|
|
181
236
|
}
|
|
182
|
-
return { success: false, error: msg };
|
|
237
|
+
return { success: false, error: stripErrorPrefix(msg) };
|
|
183
238
|
}
|
|
184
239
|
},
|
|
185
240
|
};
|
|
@@ -204,36 +259,47 @@ export function createSpatialContainsTool(
|
|
|
204
259
|
idempotentHint: true,
|
|
205
260
|
},
|
|
206
261
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
207
|
-
|
|
208
|
-
|
|
262
|
+
try {
|
|
263
|
+
const { table, spatialColumn, polygon, limit, srid } =
|
|
264
|
+
ContainsSchema.parse(params);
|
|
209
265
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
throw new Error("Invalid column name");
|
|
216
|
-
}
|
|
266
|
+
// Validate identifiers
|
|
267
|
+
validateQualifiedIdentifier(table, "table");
|
|
268
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(spatialColumn)) {
|
|
269
|
+
return { success: false, error: "Invalid column name" };
|
|
270
|
+
}
|
|
217
271
|
|
|
218
|
-
|
|
272
|
+
const escapedTable = escapeQualifiedTable(table);
|
|
219
273
|
const query = `
|
|
220
|
-
SELECT
|
|
221
|
-
FROM
|
|
274
|
+
SELECT *, ST_AsText(\`${spatialColumn}\`) as ${spatialColumn}_wkt
|
|
275
|
+
FROM ${escapedTable}
|
|
222
276
|
WHERE ST_Contains(ST_GeomFromText(?, ${String(srid)}, 'axis-order=long-lat'), \`${spatialColumn}\`)
|
|
223
277
|
LIMIT ${String(limit)}
|
|
224
278
|
`;
|
|
225
279
|
|
|
226
280
|
const result = await adapter.executeQuery(query, [polygon]);
|
|
281
|
+
// Strip raw binary spatial column from each row
|
|
282
|
+
const rows = (result.rows ?? []).map((row: Record<string, unknown>) =>
|
|
283
|
+
Object.fromEntries(
|
|
284
|
+
Object.entries(row).filter(([key]) => key !== spatialColumn),
|
|
285
|
+
),
|
|
286
|
+
);
|
|
227
287
|
return {
|
|
228
|
-
results:
|
|
229
|
-
count:
|
|
288
|
+
results: rows,
|
|
289
|
+
count: rows.length,
|
|
230
290
|
};
|
|
231
291
|
} catch (error) {
|
|
292
|
+
if (error instanceof ZodError) {
|
|
293
|
+
return { success: false, error: formatZodError(error) };
|
|
294
|
+
}
|
|
295
|
+
if (error instanceof ValidationError) {
|
|
296
|
+
return { success: false, error: error.message };
|
|
297
|
+
}
|
|
232
298
|
const msg = error instanceof Error ? error.message : String(error);
|
|
233
299
|
if (msg.includes("doesn't exist")) {
|
|
234
|
-
return { exists: false, table };
|
|
300
|
+
return { exists: false, table: paramStr(params, "table") };
|
|
235
301
|
}
|
|
236
|
-
return { success: false, error: msg };
|
|
302
|
+
return { success: false, error: stripErrorPrefix(msg) };
|
|
237
303
|
}
|
|
238
304
|
},
|
|
239
305
|
};
|
|
@@ -255,36 +321,47 @@ export function createSpatialWithinTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
255
321
|
idempotentHint: true,
|
|
256
322
|
},
|
|
257
323
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
258
|
-
|
|
259
|
-
|
|
324
|
+
try {
|
|
325
|
+
const { table, spatialColumn, geometry, limit, srid } =
|
|
326
|
+
WithinSchema.parse(params);
|
|
260
327
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
throw new Error("Invalid column name");
|
|
267
|
-
}
|
|
328
|
+
// Validate identifiers
|
|
329
|
+
validateQualifiedIdentifier(table, "table");
|
|
330
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(spatialColumn)) {
|
|
331
|
+
return { success: false, error: "Invalid column name" };
|
|
332
|
+
}
|
|
268
333
|
|
|
269
|
-
|
|
334
|
+
const escapedTable = escapeQualifiedTable(table);
|
|
270
335
|
const query = `
|
|
271
|
-
SELECT
|
|
272
|
-
FROM
|
|
336
|
+
SELECT *, ST_AsText(\`${spatialColumn}\`) as ${spatialColumn}_wkt
|
|
337
|
+
FROM ${escapedTable}
|
|
273
338
|
WHERE ST_Within(\`${spatialColumn}\`, ST_GeomFromText(?, ${String(srid)}, 'axis-order=long-lat'))
|
|
274
339
|
LIMIT ${String(limit)}
|
|
275
340
|
`;
|
|
276
341
|
|
|
277
342
|
const result = await adapter.executeQuery(query, [geometry]);
|
|
343
|
+
// Strip raw binary spatial column from each row
|
|
344
|
+
const rows = (result.rows ?? []).map((row: Record<string, unknown>) =>
|
|
345
|
+
Object.fromEntries(
|
|
346
|
+
Object.entries(row).filter(([key]) => key !== spatialColumn),
|
|
347
|
+
),
|
|
348
|
+
);
|
|
278
349
|
return {
|
|
279
|
-
results:
|
|
280
|
-
count:
|
|
350
|
+
results: rows,
|
|
351
|
+
count: rows.length,
|
|
281
352
|
};
|
|
282
353
|
} catch (error) {
|
|
354
|
+
if (error instanceof ZodError) {
|
|
355
|
+
return { success: false, error: formatZodError(error) };
|
|
356
|
+
}
|
|
357
|
+
if (error instanceof ValidationError) {
|
|
358
|
+
return { success: false, error: error.message };
|
|
359
|
+
}
|
|
283
360
|
const msg = error instanceof Error ? error.message : String(error);
|
|
284
361
|
if (msg.includes("doesn't exist")) {
|
|
285
|
-
return { exists: false, table };
|
|
362
|
+
return { exists: false, table: paramStr(params, "table") };
|
|
286
363
|
}
|
|
287
|
-
return { success: false, error: msg };
|
|
364
|
+
return { success: false, error: stripErrorPrefix(msg) };
|
|
288
365
|
}
|
|
289
366
|
},
|
|
290
367
|
};
|
|
@@ -5,33 +5,65 @@
|
|
|
5
5
|
* 2 tools: column creation and index creation.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { z } from "zod";
|
|
8
|
+
import { z, ZodError } from "zod";
|
|
9
9
|
import type { MySQLAdapter } from "../../MySQLAdapter.js";
|
|
10
10
|
import type {
|
|
11
11
|
ToolDefinition,
|
|
12
12
|
RequestContext,
|
|
13
13
|
} from "../../../../types/index.js";
|
|
14
|
+
import {
|
|
15
|
+
validateQualifiedIdentifier,
|
|
16
|
+
escapeQualifiedTable,
|
|
17
|
+
} from "../../../../utils/validators.js";
|
|
18
|
+
import { ValidationError } from "../../../../utils/validators.js";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Helpers
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/** Extract human-readable messages from a ZodError instead of raw JSON array */
|
|
25
|
+
function formatZodError(error: ZodError): string {
|
|
26
|
+
return error.issues.map((i) => i.message).join("; ");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Strip verbose adapter prefixes from MySQL error messages */
|
|
30
|
+
function stripErrorPrefix(msg: string): string {
|
|
31
|
+
return msg.replace(/^(Query failed:\s*)?(Execute failed:\s*)?/i, "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Safely extract a string field from raw params for error context */
|
|
35
|
+
function paramStr(params: unknown, key: string): string {
|
|
36
|
+
if (
|
|
37
|
+
params !== null &&
|
|
38
|
+
params !== undefined &&
|
|
39
|
+
typeof params === "object" &&
|
|
40
|
+
key in params
|
|
41
|
+
) {
|
|
42
|
+
const val = (params as Record<string, unknown>)[key];
|
|
43
|
+
return typeof val === "string" ? val : "";
|
|
44
|
+
}
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
14
47
|
|
|
15
48
|
// =============================================================================
|
|
16
49
|
// Zod Schemas
|
|
17
50
|
// =============================================================================
|
|
18
51
|
|
|
52
|
+
const VALID_GEOMETRY_TYPES = new Set([
|
|
53
|
+
"POINT",
|
|
54
|
+
"LINESTRING",
|
|
55
|
+
"POLYGON",
|
|
56
|
+
"GEOMETRY",
|
|
57
|
+
"MULTIPOINT",
|
|
58
|
+
"MULTILINESTRING",
|
|
59
|
+
"MULTIPOLYGON",
|
|
60
|
+
"GEOMETRYCOLLECTION",
|
|
61
|
+
]);
|
|
62
|
+
|
|
19
63
|
const SpatialColumnSchema = z.object({
|
|
20
64
|
table: z.string().describe("Table name"),
|
|
21
65
|
column: z.string().describe("Column name"),
|
|
22
|
-
type: z
|
|
23
|
-
.enum([
|
|
24
|
-
"POINT",
|
|
25
|
-
"LINESTRING",
|
|
26
|
-
"POLYGON",
|
|
27
|
-
"GEOMETRY",
|
|
28
|
-
"MULTIPOINT",
|
|
29
|
-
"MULTILINESTRING",
|
|
30
|
-
"MULTIPOLYGON",
|
|
31
|
-
"GEOMETRYCOLLECTION",
|
|
32
|
-
])
|
|
33
|
-
.default("GEOMETRY")
|
|
34
|
-
.describe("Geometry type"),
|
|
66
|
+
type: z.string().default("GEOMETRY").describe("Geometry type"),
|
|
35
67
|
srid: z
|
|
36
68
|
.number()
|
|
37
69
|
.default(4326)
|
|
@@ -65,25 +97,33 @@ export function createSpatialCreateColumnTool(
|
|
|
65
97
|
readOnlyHint: false,
|
|
66
98
|
},
|
|
67
99
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
68
|
-
|
|
69
|
-
|
|
100
|
+
try {
|
|
101
|
+
const { table, column, type, srid, nullable } =
|
|
102
|
+
SpatialColumnSchema.parse(params);
|
|
70
103
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
// Validate identifiers
|
|
105
|
+
validateQualifiedIdentifier(table, "table");
|
|
106
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(column)) {
|
|
107
|
+
return { success: false, error: "Invalid column name" };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Validate geometry type
|
|
111
|
+
const upperType = type.toUpperCase();
|
|
112
|
+
if (!VALID_GEOMETRY_TYPES.has(upperType)) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: `Invalid type: '${type}' — expected one of: ${[...VALID_GEOMETRY_TYPES].join(", ")}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
78
118
|
|
|
79
|
-
try {
|
|
80
119
|
const nullClause = nullable ? "" : " NOT NULL";
|
|
81
120
|
const sridClause = srid ? ` SRID ${String(srid)}` : "";
|
|
82
121
|
|
|
83
122
|
await adapter.executeQuery(
|
|
84
|
-
`ALTER TABLE
|
|
123
|
+
`ALTER TABLE ${escapeQualifiedTable(table)} ADD COLUMN \`${column}\` ${upperType}${sridClause}${nullClause}`,
|
|
85
124
|
);
|
|
86
125
|
|
|
126
|
+
adapter.clearSchemaCache();
|
|
87
127
|
return {
|
|
88
128
|
success: true,
|
|
89
129
|
table,
|
|
@@ -93,17 +133,25 @@ export function createSpatialCreateColumnTool(
|
|
|
93
133
|
nullable,
|
|
94
134
|
};
|
|
95
135
|
} catch (error) {
|
|
136
|
+
if (error instanceof ZodError) {
|
|
137
|
+
return { success: false, error: formatZodError(error) };
|
|
138
|
+
}
|
|
139
|
+
if (error instanceof ValidationError) {
|
|
140
|
+
return { success: false, error: error.message };
|
|
141
|
+
}
|
|
96
142
|
const msg = error instanceof Error ? error.message : String(error);
|
|
97
143
|
if (msg.includes("doesn't exist")) {
|
|
98
|
-
return { exists: false, table };
|
|
144
|
+
return { exists: false, table: paramStr(params, "table") };
|
|
99
145
|
}
|
|
100
146
|
if (msg.includes("Duplicate column name")) {
|
|
147
|
+
const col = paramStr(params, "column");
|
|
148
|
+
const tbl = paramStr(params, "table");
|
|
101
149
|
return {
|
|
102
150
|
success: false,
|
|
103
|
-
|
|
151
|
+
error: `Column '${col}' already exists on table '${tbl}'`,
|
|
104
152
|
};
|
|
105
153
|
}
|
|
106
|
-
return { success: false, error: msg };
|
|
154
|
+
return { success: false, error: stripErrorPrefix(msg) };
|
|
107
155
|
}
|
|
108
156
|
},
|
|
109
157
|
};
|
|
@@ -127,27 +175,32 @@ export function createSpatialCreateIndexTool(
|
|
|
127
175
|
readOnlyHint: false,
|
|
128
176
|
},
|
|
129
177
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
130
|
-
|
|
178
|
+
try {
|
|
179
|
+
const { table, column, indexName } = SpatialIndexSchema.parse(params);
|
|
131
180
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
throw new Error("Invalid column name");
|
|
138
|
-
}
|
|
181
|
+
// Validate identifiers
|
|
182
|
+
validateQualifiedIdentifier(table, "table");
|
|
183
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(column)) {
|
|
184
|
+
return { success: false, error: "Invalid column name" };
|
|
185
|
+
}
|
|
139
186
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
187
|
+
// For qualified names (schema.table), split for information_schema queries
|
|
188
|
+
const parts = table.split(".");
|
|
189
|
+
const bareTable = parts.length > 1 ? parts[1] : parts[0];
|
|
190
|
+
const schemaClause =
|
|
191
|
+
parts.length > 1 ? "TABLE_SCHEMA = ?" : "TABLE_SCHEMA = DATABASE()";
|
|
192
|
+
const schemaParams = parts.length > 1 ? [parts[0]] : [];
|
|
193
|
+
|
|
194
|
+
const idxName = indexName ?? `idx_spatial_${bareTable}_${column}`;
|
|
195
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(idxName)) {
|
|
196
|
+
return { success: false, error: "Invalid index name" };
|
|
197
|
+
}
|
|
144
198
|
|
|
145
|
-
try {
|
|
146
199
|
// Check if column is nullable - SPATIAL indexes require NOT NULL
|
|
147
200
|
const colInfo = await adapter.executeQuery(
|
|
148
201
|
`SELECT IS_NULLABLE, DATA_TYPE FROM information_schema.COLUMNS
|
|
149
|
-
WHERE
|
|
150
|
-
[
|
|
202
|
+
WHERE ${schemaClause} AND TABLE_NAME = ? AND COLUMN_NAME = ?`,
|
|
203
|
+
[...schemaParams, bareTable, column],
|
|
151
204
|
);
|
|
152
205
|
|
|
153
206
|
const colRow = colInfo.rows?.[0];
|
|
@@ -155,20 +208,22 @@ export function createSpatialCreateIndexTool(
|
|
|
155
208
|
const isNullable = colRow["IS_NULLABLE"] === "YES";
|
|
156
209
|
const dataType = String(colRow["DATA_TYPE"]).toUpperCase();
|
|
157
210
|
if (isNullable) {
|
|
158
|
-
|
|
159
|
-
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
error:
|
|
214
|
+
`Cannot create SPATIAL index on nullable column '${column}'. ` +
|
|
160
215
|
`Alter the column to NOT NULL first: ` +
|
|
161
|
-
`ALTER TABLE
|
|
162
|
-
|
|
216
|
+
`ALTER TABLE ${escapeQualifiedTable(table)} MODIFY \`${column}\` ${dataType} NOT NULL`,
|
|
217
|
+
};
|
|
163
218
|
}
|
|
164
219
|
}
|
|
165
220
|
|
|
166
221
|
// Check if a SPATIAL index already exists on this column (any name)
|
|
167
222
|
const existingIdx = await adapter.executeQuery(
|
|
168
223
|
`SELECT INDEX_NAME FROM information_schema.STATISTICS
|
|
169
|
-
WHERE
|
|
224
|
+
WHERE ${schemaClause} AND TABLE_NAME = ? AND COLUMN_NAME = ? AND INDEX_TYPE = 'SPATIAL'
|
|
170
225
|
LIMIT 1`,
|
|
171
|
-
[
|
|
226
|
+
[...schemaParams, bareTable, column],
|
|
172
227
|
);
|
|
173
228
|
|
|
174
229
|
const existingRow = existingIdx.rows?.[0];
|
|
@@ -176,14 +231,15 @@ export function createSpatialCreateIndexTool(
|
|
|
176
231
|
const existingName = String(existingRow["INDEX_NAME"]);
|
|
177
232
|
return {
|
|
178
233
|
success: false,
|
|
179
|
-
|
|
234
|
+
error: `Spatial index '${existingName}' already exists on column '${column}' of table '${table}'`,
|
|
180
235
|
};
|
|
181
236
|
}
|
|
182
237
|
|
|
183
238
|
await adapter.executeQuery(
|
|
184
|
-
`CREATE SPATIAL INDEX \`${idxName}\` ON
|
|
239
|
+
`CREATE SPATIAL INDEX \`${idxName}\` ON ${escapeQualifiedTable(table)}(\`${column}\`)`,
|
|
185
240
|
);
|
|
186
241
|
|
|
242
|
+
adapter.clearSchemaCache();
|
|
187
243
|
return {
|
|
188
244
|
success: true,
|
|
189
245
|
table,
|
|
@@ -191,20 +247,30 @@ export function createSpatialCreateIndexTool(
|
|
|
191
247
|
indexName: idxName,
|
|
192
248
|
};
|
|
193
249
|
} catch (error) {
|
|
250
|
+
if (error instanceof ZodError) {
|
|
251
|
+
return { success: false, error: formatZodError(error) };
|
|
252
|
+
}
|
|
253
|
+
if (error instanceof ValidationError) {
|
|
254
|
+
return { success: false, error: error.message };
|
|
255
|
+
}
|
|
194
256
|
const msg = error instanceof Error ? error.message : String(error);
|
|
257
|
+
const tbl = paramStr(params, "table");
|
|
195
258
|
if (msg.includes("doesn't exist")) {
|
|
196
|
-
return { exists: false, table };
|
|
259
|
+
return { exists: false, table: tbl };
|
|
197
260
|
}
|
|
198
261
|
if (msg.includes("Cannot create SPATIAL index on nullable column")) {
|
|
199
|
-
return { success: false,
|
|
262
|
+
return { success: false, error: stripErrorPrefix(msg) };
|
|
200
263
|
}
|
|
264
|
+
const idxFromParams = paramStr(params, "indexName");
|
|
265
|
+
const idx =
|
|
266
|
+
idxFromParams || `idx_spatial_${tbl}_${paramStr(params, "column")}`;
|
|
201
267
|
if (msg.includes("Duplicate key name")) {
|
|
202
268
|
return {
|
|
203
269
|
success: false,
|
|
204
|
-
|
|
270
|
+
error: `Index '${idx}' already exists on table '${tbl}'`,
|
|
205
271
|
};
|
|
206
272
|
}
|
|
207
|
-
return { success: false, error: msg };
|
|
273
|
+
return { success: false, error: stripErrorPrefix(msg) };
|
|
208
274
|
}
|
|
209
275
|
},
|
|
210
276
|
};
|
|
@@ -67,16 +67,17 @@ describe("Comparative Stats Tools", () => {
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
it("should validate inputs", async () => {
|
|
70
|
-
await
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
).
|
|
70
|
+
const result: any = await correlationTool.handler(
|
|
71
|
+
{
|
|
72
|
+
table: "bad;table",
|
|
73
|
+
column1: "x",
|
|
74
|
+
column2: "y",
|
|
75
|
+
},
|
|
76
|
+
{} as any,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(result.success).toBe(false);
|
|
80
|
+
expect(result.error).toContain("Invalid table name");
|
|
80
81
|
});
|
|
81
82
|
});
|
|
82
83
|
|
|
@@ -95,6 +96,7 @@ describe("Comparative Stats Tools", () => {
|
|
|
95
96
|
{} as any,
|
|
96
97
|
);
|
|
97
98
|
|
|
99
|
+
expect(result.success).toBe(false);
|
|
98
100
|
expect(result.error).toContain("Insufficient data");
|
|
99
101
|
});
|
|
100
102
|
|