bazis 0.96.1
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/LICENSE +21 -0
- package/README.md +146 -0
- package/cli/build.ts +147 -0
- package/cli/codegen.ts +40 -0
- package/cli/generateModule.ts +187 -0
- package/cli/generateProject.ts +183 -0
- package/cli/main.ts +125 -0
- package/cli/moduleRegistration.ts +127 -0
- package/cli/naming.ts +124 -0
- package/cli/parseCli.ts +151 -0
- package/cli/templates/module.ts +497 -0
- package/cli/templates/pack.ts +76 -0
- package/cli/templates/passport.ts +178 -0
- package/core/agent/AgentContextBuilder.ts +567 -0
- package/core/agent/AgentDataDefinition.ts +8 -0
- package/core/agent/AgentRegistry.ts +539 -0
- package/core/agent/AgentRuntime.ts +47 -0
- package/core/agent/AgentToolExecutor.ts +2130 -0
- package/core/agent/AgentToolHooks.ts +39 -0
- package/core/agent/ModelProvider.ts +21 -0
- package/core/agent/README.md +229 -0
- package/core/agent/Tool.contract.ts +20 -0
- package/core/agent/decorators/Agent.ts +11 -0
- package/core/agent/decorators/Prompt.ts +11 -0
- package/core/agent/decorators/Task.ts +29 -0
- package/core/agent/decorators/Tool.ts +12 -0
- package/core/agent/errors.ts +43 -0
- package/core/agent/generated/catalog.ts +26 -0
- package/core/agent/index.ts +170 -0
- package/core/agent/internal/AgentExecutionDriver.ts +1497 -0
- package/core/agent/internal/AgentExecutionState.ts +775 -0
- package/core/agent/internal/AgentJsonSchema.ts +150 -0
- package/core/agent/internal/AgentModelBinding.ts +57 -0
- package/core/agent/internal/Schema.contract.ts +36 -0
- package/core/agent/internal/ToolAudit.projector.ts +163 -0
- package/core/agent/internal/ToolContract.validator.ts +102 -0
- package/core/agent/metadata.ts +398 -0
- package/core/agent/moduleContributions-v1.ts +113 -0
- package/core/agent/semantic.ts +903 -0
- package/core/agent/session/codec.ts +33 -0
- package/core/agent/session/contracts.ts +96 -0
- package/core/agent/session/entities.ts +84 -0
- package/core/agent/session/protector.ts +92 -0
- package/core/app/index.ts +5 -0
- package/core/app/runApp.ts +293 -0
- package/core/app/ui/index.ts +29 -0
- package/core/app/ui/uiProfileResolver.ts +966 -0
- package/core/app/ui/uiSurfaceHosting.ts +38 -0
- package/core/app/ui/uiSurfaceHttp.ts +476 -0
- package/core/background/BackgroundService.ts +223 -0
- package/core/background/backgroundModule.ts +42 -0
- package/core/background/decorator.ts +43 -0
- package/core/background/delay.ts +27 -0
- package/core/background/index.ts +15 -0
- package/core/cache/COMPATIBILITY.md +13 -0
- package/core/cache/ICache.ts +71 -0
- package/core/cache/MemoryCache.ts +394 -0
- package/core/cache/README.md +83 -0
- package/core/cache/SPEC.md +1355 -0
- package/core/cache/cacheModule.ts +21 -0
- package/core/cache/decorators/Cacheable.ts +38 -0
- package/core/cache/decorators/CacheableRedis.ts +39 -0
- package/core/cache/decorators/OutputCache.ts +33 -0
- package/core/cache/decorators/OutputRedisCache.ts +33 -0
- package/core/cache/decorators/cacheableMetadata.ts +78 -0
- package/core/cache/decorators/cacheableRedisMetadata.ts +81 -0
- package/core/cache/decorators/outputCacheMetadata.ts +71 -0
- package/core/cache/decorators/outputRedisCacheMetadata.ts +77 -0
- package/core/cache/di/classProviderHook.ts +26 -0
- package/core/cache/distributed/CacheCodec.ts +5 -0
- package/core/cache/distributed/DistributedCache.ts +403 -0
- package/core/cache/distributed/DistributedCacheDriver.ts +83 -0
- package/core/cache/distributed/DistributedCacheStores.ts +28 -0
- package/core/cache/distributed/IDistributedCache.ts +40 -0
- package/core/cache/distributed/NamedCacheRegistry.ts +23 -0
- package/core/cache/distributed/codecs.ts +32 -0
- package/core/cache/distributed/index.ts +19 -0
- package/core/cache/errors/CacheError.ts +16 -0
- package/core/cache/http/CachedHttpPayload.ts +213 -0
- package/core/cache/http/applyClientCacheHeaders.ts +61 -0
- package/core/cache/http/buildOutputCacheKey.ts +154 -0
- package/core/cache/http/buildRedisOutputCacheKey.ts +16 -0
- package/core/cache/http/composeOutputCache.ts +130 -0
- package/core/cache/http/outputCacheMiddleware.ts +111 -0
- package/core/cache/http/outputCacheSecurityWarning.ts +58 -0
- package/core/cache/http/outputRedisCacheMiddleware.ts +118 -0
- package/core/cache/index.ts +97 -0
- package/core/cache/internal/AutomaticCacheKey.ts +106 -0
- package/core/cache/internal/CacheKeyGuard.ts +56 -0
- package/core/cache/internal/buildCacheProviders.ts +146 -0
- package/core/cache/internal/normalizeCacheKey.ts +9 -0
- package/core/cache/internal/resolveCachePolicy.ts +32 -0
- package/core/cache/internal/resolveCacheableRedisPolicy.ts +17 -0
- package/core/cache/internal/resolveOutputRedisCachePolicy.ts +17 -0
- package/core/cache/providers/cachedProviders.ts +68 -0
- package/core/cache/services/cacheProxy.ts +243 -0
- package/core/cache/tokens/CACHE_POLICIES.ts +5 -0
- package/core/cache/tokens/DISTRIBUTED_CACHE.ts +30 -0
- package/core/cache/types/CacheEntry.ts +7 -0
- package/core/cache/types/CacheOptions.ts +60 -0
- package/core/cache/types/CachePolicy.ts +74 -0
- package/core/cache/types/CacheableRedisOptions.ts +23 -0
- package/core/cache/types/OutputRedisCacheOptions.ts +25 -0
- package/core/di/README.md +104 -0
- package/core/di/ServiceCollection.ts +185 -0
- package/core/di/ServiceProvider.ts +689 -0
- package/core/di/ServiceScope.ts +61 -0
- package/core/di/container.ts +38 -0
- package/core/di/errors/AmbiguousNamedDependencyError.ts +10 -0
- package/core/di/errors/AsyncResolutionRequiredError.ts +12 -0
- package/core/di/errors/CircularDependencyError.ts +10 -0
- package/core/di/errors/ClassDependenciesMismatchError.ts +15 -0
- package/core/di/errors/DiError.ts +6 -0
- package/core/di/errors/InvalidProviderError.ts +9 -0
- package/core/di/errors/ModuleEncapsulationError.ts +7 -0
- package/core/di/errors/ModuleOwnedProviderConflictError.ts +8 -0
- package/core/di/errors/NamedDependencyNotFoundError.ts +7 -0
- package/core/di/errors/OptionsValidationError.ts +7 -0
- package/core/di/errors/ProviderNotFoundError.ts +10 -0
- package/core/di/errors/ScopeDisposedError.ts +7 -0
- package/core/di/errors/ScopedServiceFromRootError.ts +9 -0
- package/core/di/errors/ServiceValidationError.ts +7 -0
- package/core/di/errors/index.ts +14 -0
- package/core/di/extensions/activator.ts +39 -0
- package/core/di/extensions/application.ts +117 -0
- package/core/di/extensions/hosted-service.ts +124 -0
- package/core/di/extensions/index.ts +23 -0
- package/core/di/extensions/options-reloadable.ts +137 -0
- package/core/di/extensions/options.ts +91 -0
- package/core/di/extensions/service-provider-token.ts +21 -0
- package/core/di/index.ts +97 -0
- package/core/di/internal/GraphValidator.ts +119 -0
- package/core/di/internal/HostedServicePlan.ts +158 -0
- package/core/di/internal/NamedTokenIndex.ts +55 -0
- package/core/di/internal/OpenGenericRegistration.ts +14 -0
- package/core/di/internal/ResolutionPlan.ts +18 -0
- package/core/di/internal/ResolutionScopeState.ts +19 -0
- package/core/di/internal/ResolutionTracker.ts +153 -0
- package/core/di/internal/ScopeLifecycle.ts +159 -0
- package/core/di/internal/ServiceRegistration.ts +14 -0
- package/core/di/internal/ServiceRegistry.ts +252 -0
- package/core/di/internal/classDeps.ts +38 -0
- package/core/di/internal/disposal.ts +58 -0
- package/core/di/internal/providerDeps.ts +26 -0
- package/core/di/module/DI.ts +146 -0
- package/core/di/module/Global.ts +25 -0
- package/core/di/module/Module.ts +44 -0
- package/core/di/module/ModuleRegistrar.ts +69 -0
- package/core/di/module/autoDeps.ts +39 -0
- package/core/di/module/classProviderHooks.ts +66 -0
- package/core/di/module/collectModuleConfigs.ts +57 -0
- package/core/di/module/collectModuleControllers.ts +53 -0
- package/core/di/module/collectModuleUiProfiles.ts +53 -0
- package/core/di/module/createContainer.ts +261 -0
- package/core/di/module/encapsulation.ts +416 -0
- package/core/di/module/encapsulationHooks.ts +36 -0
- package/core/di/module/generatedProviderAttachments.ts +102 -0
- package/core/di/module/index.ts +41 -0
- package/core/di/module/moduleExtensions.ts +35 -0
- package/core/di/module/moduleNameBinding.ts +136 -0
- package/core/di/module/moduleOwnedProviderContributors.ts +409 -0
- package/core/di/module/providerSelection.ts +90 -0
- package/core/di/module/shortcuts.ts +111 -0
- package/core/di/module/types/BazisModule.ts +71 -0
- package/core/di/module/types/DiRegistrar.ts +24 -0
- package/core/di/module/types/index.ts +2 -0
- package/core/di/provider/index.ts +26 -0
- package/core/di/provider/providerGuards.ts +17 -0
- package/core/di/provider/types/AsyncFactoryProvider.ts +15 -0
- package/core/di/provider/types/ClassProvider.ts +18 -0
- package/core/di/provider/types/FactoryProvider.ts +20 -0
- package/core/di/provider/types/KeyedDependency.ts +20 -0
- package/core/di/provider/types/LazyDependency.ts +31 -0
- package/core/di/provider/types/NamedDependency.ts +16 -0
- package/core/di/provider/types/Provider.ts +10 -0
- package/core/di/provider/types/ProviderDefinition.ts +11 -0
- package/core/di/provider/types/ProviderDependencyList.ts +11 -0
- package/core/di/provider/types/ProviderLifetime.ts +1 -0
- package/core/di/provider/types/ProviderOwnership.ts +7 -0
- package/core/di/provider/types/ResolvedDeps.ts +18 -0
- package/core/di/provider/types/ValueProvider.ts +6 -0
- package/core/di/provider/types/index.ts +13 -0
- package/core/di/token.ts +84 -0
- package/core/di/types/BuildServiceProviderOptions.ts +4 -0
- package/core/di/types/ServiceKey.ts +1 -0
- package/core/di/types/ServiceResolver.ts +16 -0
- package/core/di/types/index.ts +3 -0
- package/core/generatedFingerprint.ts +60 -0
- package/core/generatedRuntime.ts +187 -0
- package/core/grpc/Grpc.module.ts +23 -0
- package/core/grpc/GrpcBinding.contract.ts +35 -0
- package/core/grpc/GrpcCall.ts +73 -0
- package/core/grpc/GrpcClient.ts +172 -0
- package/core/grpc/GrpcClientCall.ts +267 -0
- package/core/grpc/GrpcError.ts +19 -0
- package/core/grpc/GrpcRequest.binding.ts +45 -0
- package/core/grpc/GrpcServer.ts +204 -0
- package/core/grpc/GrpcStatus.ts +8 -0
- package/core/grpc/GrpcTransport.ts +174 -0
- package/core/grpc/Metadata.ts +73 -0
- package/core/grpc/ProtoCodec.ts +225 -0
- package/core/grpc/ProtoParser.ts +192 -0
- package/core/grpc/ProtoReader.ts +34 -0
- package/core/grpc/ProtoSchema.ts +52 -0
- package/core/grpc/ServerCredentials.ts +30 -0
- package/core/grpc/clientContracts.ts +39 -0
- package/core/grpc/contracts.ts +45 -0
- package/core/grpc/decorators.ts +82 -0
- package/core/grpc/grpcClientProvider.ts +9 -0
- package/core/grpc/index.ts +17 -0
- package/core/grpc/moduleControllers.ts +38 -0
- package/core/grpc/protobuf.ts +80 -0
- package/core/grpc/serviceDefinition.ts +20 -0
- package/core/http/Authorization/Authorize.ts +52 -0
- package/core/http/Authorization/authorizeComposer.ts +31 -0
- package/core/http/Authorization/metadata.ts +158 -0
- package/core/http/Binding/ParameterBinder.ts +109 -0
- package/core/http/Binding/RequestModel.ts +27 -0
- package/core/http/Binding/autoBindings.ts +125 -0
- package/core/http/Binding/bindings.ts +19 -0
- package/core/http/Binding/convert.ts +48 -0
- package/core/http/Binding/generatedSpec.ts +18 -0
- package/core/http/Binding/listModelRegistry.ts +47 -0
- package/core/http/Binding/modelBinder.ts +345 -0
- package/core/http/Binding/modelValidator.ts +30 -0
- package/core/http/Binding/requestModelRegistry.ts +117 -0
- package/core/http/Decorators/attributes.ts +92 -0
- package/core/http/Decorators/controller.ts +24 -0
- package/core/http/Decorators/metadata.ts +107 -0
- package/core/http/Decorators/routes.ts +131 -0
- package/core/http/Errors/HttpError.ts +92 -0
- package/core/http/HttpContext/HttpContext.ts +148 -0
- package/core/http/HttpContext/ResponseBuilder.ts +37 -0
- package/core/http/HttpContext/inspectableRedirects.ts +47 -0
- package/core/http/HttpContext/principal.ts +24 -0
- package/core/http/HttpContext/responseLifetime.ts +45 -0
- package/core/http/HttpServer.ts +627 -0
- package/core/http/Middleware/accessLog.ts +90 -0
- package/core/http/Middleware/cors.ts +160 -0
- package/core/http/Middleware/errorHandler.ts +100 -0
- package/core/http/Middleware/pipeline.ts +21 -0
- package/core/http/Middleware/rateLimit.ts +104 -0
- package/core/http/Middleware/securityHeaders.ts +118 -0
- package/core/http/Middleware/types.ts +19 -0
- package/core/http/OpenApi/generatedOpenApiRegistry.ts +102 -0
- package/core/http/OpenApi/openApiDocs.ts +75 -0
- package/core/http/OpenApi/openApiDocument.ts +356 -0
- package/core/http/README.md +139 -0
- package/core/http/Results/HttpResult.ts +19 -0
- package/core/http/Results/normalize.ts +93 -0
- package/core/http/Results/results.ts +70 -0
- package/core/http/Routing/Router.ts +201 -0
- package/core/http/Routing/RouterBuilder.ts +184 -0
- package/core/http/Routing/template.ts +112 -0
- package/core/http/SPEC.md +981 -0
- package/core/http/Versioning/VersionReader.ts +20 -0
- package/core/http/WebSocketUpgrade.ts +29 -0
- package/core/http/composeRouteMiddlewareComposers.ts +21 -0
- package/core/http/correlation/createCorrelationIdMiddleware.ts +79 -0
- package/core/http/httpModule.ts +48 -0
- package/core/http/index.ts +140 -0
- package/core/http/middlewareTokens.ts +48 -0
- package/core/http/options.ts +134 -0
- package/core/http-client/index.ts +105 -0
- package/core/infra/Infra.ts +158 -0
- package/core/infra/InfraConnector.ts +86 -0
- package/core/infra/InfraLifecycle.ts +86 -0
- package/core/infra/cache/RedisDistributedCacheBackend.ts +88 -0
- package/core/infra/cache/RedisDistributedCacheDriver.ts +186 -0
- package/core/infra/cache/index.ts +7 -0
- package/core/infra/connectorConfig.ts +29 -0
- package/core/infra/connectorIdentity.ts +39 -0
- package/core/infra/connectors/codex/CodexAppServer.ts +146 -0
- package/core/infra/connectors/codex/CodexAppServerClient.ts +258 -0
- package/core/infra/connectors/codex/CodexPolicy.ts +114 -0
- package/core/infra/connectors/codex/CodexTurn.ts +133 -0
- package/core/infra/connectors/codex/contracts.ts +82 -0
- package/core/infra/connectors/codex.ts +26 -0
- package/core/infra/connectors/llm.ts +78 -0
- package/core/infra/connectors/llmRouter.ts +443 -0
- package/core/infra/connectors/openaiCompatible.ts +590 -0
- package/core/infra/connectors/openaiTextStream.ts +134 -0
- package/core/infra/connectors/opensearch.ts +307 -0
- package/core/infra/connectors/postgres.ts +105 -0
- package/core/infra/connectors/redis.ts +132 -0
- package/core/infra/index.ts +58 -0
- package/core/kernel/ApplicationLifetime.ts +91 -0
- package/core/kernel/Bazis.ts +73 -0
- package/core/kernel/Environment.ts +53 -0
- package/core/kernel/Kernel.ts +391 -0
- package/core/kernel/KernelBuilder.ts +172 -0
- package/core/kernel/LifecycleCoordinator.ts +204 -0
- package/core/kernel/README.md +66 -0
- package/core/kernel/SupervisedHostedService.ts +147 -0
- package/core/kernel/config/ConfigRegistry.ts +48 -0
- package/core/kernel/config/Configuration.ts +110 -0
- package/core/kernel/config/README.md +62 -0
- package/core/kernel/config/Secret.ts +29 -0
- package/core/kernel/config/addConfigOptions.ts +63 -0
- package/core/kernel/config/defineConfig.ts +211 -0
- package/core/kernel/config/sources.ts +123 -0
- package/core/kernel/correlation/requestContext.ts +57 -0
- package/core/kernel/errors/ConfigKeyMissingError.ts +7 -0
- package/core/kernel/errors/KernelError.ts +7 -0
- package/core/kernel/errors/ShutdownTimeoutError.ts +7 -0
- package/core/kernel/errors/StartupAbortedError.ts +7 -0
- package/core/kernel/errors/StartupTimeoutError.ts +7 -0
- package/core/kernel/errors/index.ts +5 -0
- package/core/kernel/events/EventBus.ts +140 -0
- package/core/kernel/events/EventToken.ts +17 -0
- package/core/kernel/events/eventSubscribers.ts +126 -0
- package/core/kernel/events/kernelEvents.ts +16 -0
- package/core/kernel/events/onEvent.ts +32 -0
- package/core/kernel/health/HealthCheckContracts.ts +42 -0
- package/core/kernel/health/HealthService.ts +93 -0
- package/core/kernel/index.ts +96 -0
- package/core/kernel/internal/awaitAbortable.ts +18 -0
- package/core/kernel/internal/awaitShutdown.ts +21 -0
- package/core/kernel/internal/reportDiagnosticFailure.ts +8 -0
- package/core/kernel/internal/validateTimeout.ts +8 -0
- package/core/kernel/lifecycleHooks.ts +18 -0
- package/core/kernel/logging/ConsoleLogger.ts +68 -0
- package/core/kernel/logging/Logger.ts +22 -0
- package/core/kernel/types/ConfigSource.ts +9 -0
- package/core/kernel/types/EnvironmentName.ts +1 -0
- package/core/kernel/types/KernelOptions.ts +23 -0
- package/core/kernel/types/LifecycleHook.ts +18 -0
- package/core/kernel/types/index.ts +4 -0
- package/core/orm/DATABASE_PROVIDER.ts +5 -0
- package/core/orm/OrmHostedPlan.validator.ts +55 -0
- package/core/orm/OrmLifecycle.ts +146 -0
- package/core/orm/OrmOwnedStoreLifecycle.ts +96 -0
- package/core/orm/buildOrmProviders.ts +152 -0
- package/core/orm/databaseConnector.ts +108 -0
- package/core/orm/encapsulationHook.ts +19 -0
- package/core/orm/index.ts +95 -0
- package/core/orm/listQuery.ts +198 -0
- package/core/orm/ormModule.ts +173 -0
- package/core/orm/ownedStoreContributions.ts +195 -0
- package/core/orm/registerRepositories.ts +27 -0
- package/core/orm/repository.ts +45 -0
- package/core/orm/strictSchemaHostedIdentity.ts +17 -0
- package/core/scripts/agent-codegen.ts +1166 -0
- package/core/scripts/di-generate-target.ts +90 -0
- package/core/scripts/di-generate.ts +1837 -0
- package/core/scripts/orm-predicate-codegen.ts +33 -0
- package/core/scripts/request-model-codegen.ts +326 -0
- package/core/websocket/DESIGN.md +297 -0
- package/core/websocket/WsRateLimiter.ts +101 -0
- package/core/websocket/adapter/adapter.interface.ts +45 -0
- package/core/websocket/adapter/in-memory.adapter.ts +190 -0
- package/core/websocket/adapter/redis-delivery-scripts.ts +135 -0
- package/core/websocket/adapter/redis-delivery-store.ts +86 -0
- package/core/websocket/adapter/redis-operations.ts +33 -0
- package/core/websocket/adapter/redis.adapter.ts +218 -0
- package/core/websocket/codec/binary.codec.ts +87 -0
- package/core/websocket/codec/json.codec.ts +34 -0
- package/core/websocket/codec/packet-codec.interface.ts +17 -0
- package/core/websocket/decorators.ts +165 -0
- package/core/websocket/explorer.ts +126 -0
- package/core/websocket/index.ts +78 -0
- package/core/websocket/middleware.ts +146 -0
- package/core/websocket/packet-codec.ts +75 -0
- package/core/websocket/reliable-delivery.ts +56 -0
- package/core/websocket/session-creation.ts +16 -0
- package/core/websocket/session-manager.ts +856 -0
- package/core/websocket/socket-wrapper.ts +314 -0
- package/core/websocket/topic-cache.ts +59 -0
- package/core/websocket/types.ts +122 -0
- package/core/websocket/websocketModule.ts +96 -0
- package/core/websocket/ws-dispatch.ts +123 -0
- package/core/websocket/ws-server.ts +1707 -0
- package/index.ts +281 -0
- package/library/boundary/diagnostics-v1.ts +136 -0
- package/library/boundary/index.ts +73 -0
- package/library/boundary/json/decodeBoundedJsonV1.ts +518 -0
- package/library/boundary/json/limits-v1.ts +46 -0
- package/library/boundary/json/normalizeBoundedJsonV1.ts +256 -0
- package/library/boundary/json/types-v1.ts +11 -0
- package/library/boundary/schema/boundarySchemaHashV1.ts +6 -0
- package/library/boundary/schema/collectBoundaryValueDiagnosticsV1.ts +325 -0
- package/library/boundary/schema/decodeBoundarySchemaV1.ts +646 -0
- package/library/boundary/schema/index.ts +8 -0
- package/library/boundary/schema/types-v1.ts +99 -0
- package/library/boundary/schema/validateBoundaryValueV1.ts +58 -0
- package/library/boundary/serialization/canonicalJsonV1.ts +66 -0
- package/library/http-client/HttpClient.ts +969 -0
- package/library/http-client/HttpClientFactory.ts +45 -0
- package/library/http-client/errors.ts +68 -0
- package/library/http-client/index.ts +43 -0
- package/library/http-client/interceptors.ts +40 -0
- package/library/http-client/progress.ts +62 -0
- package/library/http-client/redirectProtocol.ts +6 -0
- package/library/http-client/serialize.ts +133 -0
- package/library/http-client/types.ts +174 -0
- package/library/jsonapi/ListQueryError.ts +25 -0
- package/library/jsonapi/ListRequest.ts +37 -0
- package/library/jsonapi/decorators.ts +161 -0
- package/library/jsonapi/document.ts +124 -0
- package/library/jsonapi/index.ts +40 -0
- package/library/jsonapi/parseListQuery.ts +291 -0
- package/library/jsonapi/types.ts +125 -0
- package/library/jwt/JwtEncoder.ts +40 -0
- package/library/jwt/JwtKeyRing.ts +164 -0
- package/library/jwt/JwtValidator.ts +210 -0
- package/library/jwt/TokenIssuer.ts +149 -0
- package/library/jwt/TokenService.ts +51 -0
- package/library/jwt/base64url.ts +61 -0
- package/library/jwt/claims.ts +36 -0
- package/library/jwt/errors.ts +44 -0
- package/library/jwt/index.ts +59 -0
- package/library/jwt/limits.ts +8 -0
- package/library/jwt/signing/Hs256Algorithm.ts +62 -0
- package/library/jwt/signing/Rs256Algorithm.ts +133 -0
- package/library/jwt/signing/SigningAlgorithm.ts +18 -0
- package/library/jwt/signing/pem.ts +30 -0
- package/library/openapi/codegen.ts +875 -0
- package/library/openapi/document.ts +326 -0
- package/library/openapi/index.ts +13 -0
- package/library/openapi/types.ts +69 -0
- package/library/openapi/ui.ts +182 -0
- package/library/orm/DatabaseFacade.ts +209 -0
- package/library/orm/DbContext.ts +107 -0
- package/library/orm/DbContextFactory.ts +25 -0
- package/library/orm/DbContextOptions.ts +33 -0
- package/library/orm/Metadata/DynamicModelBuilder.ts +497 -0
- package/library/orm/Metadata/KeyTuple.ts +36 -0
- package/library/orm/Metadata/ModelBuilder.ts +299 -0
- package/library/orm/Metadata/OrmModel.ts +80 -0
- package/library/orm/Metadata/ValueConverter.ts +57 -0
- package/library/orm/Metadata/conventions.ts +54 -0
- package/library/orm/Metadata/decorators.ts +396 -0
- package/library/orm/Metadata/types.ts +140 -0
- package/library/orm/Providers/OwnedStoreCatalog.reader.ts +1802 -0
- package/library/orm/Providers/PostgresDialect.ts +234 -0
- package/library/orm/Providers/PostgresProvider.ts +1494 -0
- package/library/orm/Providers/PostgresSchema.decoder.ts +107 -0
- package/library/orm/Providers/limits.ts +29 -0
- package/library/orm/Providers/operationDeadline.ts +43 -0
- package/library/orm/Providers/ormOwnedStoreRuntime.ts +19 -0
- package/library/orm/Providers/ormTransactionRuntime.ts +117 -0
- package/library/orm/Providers/propertyConversion.ts +14 -0
- package/library/orm/Providers/resolvedForeignKey.ts +16 -0
- package/library/orm/Providers/transactionCallbacks.ts +45 -0
- package/library/orm/Providers/transactionOutcome.ts +82 -0
- package/library/orm/Providers/transactionScopes.ts +78 -0
- package/library/orm/Providers/types.ts +207 -0
- package/library/orm/Query/DbSet.ts +78 -0
- package/library/orm/Query/EntityQuery.ts +247 -0
- package/library/orm/Query/ImmediateMutations.ts +226 -0
- package/library/orm/Query/IncludeLoader.ts +200 -0
- package/library/orm/Query/QueryPlan.ts +55 -0
- package/library/orm/Query/SqlTranslator.ts +195 -0
- package/library/orm/Query/conditions.ts +127 -0
- package/library/orm/Query/materialize.ts +39 -0
- package/library/orm/README.md +256 -0
- package/library/orm/Repository/README.md +684 -0
- package/library/orm/Repository/Repository.ts +68 -0
- package/library/orm/Saving/CommandBuilder.ts +139 -0
- package/library/orm/Saving/ExecutionStrategy.ts +128 -0
- package/library/orm/Saving/SaveExecutor.ts +413 -0
- package/library/orm/Saving/applyConventions.ts +64 -0
- package/library/orm/Schema/CheckExpression.ts +327 -0
- package/library/orm/Schema/ExactSchemaVerifier.ts +168 -0
- package/library/orm/Schema/ExpectedSchema.ts +43 -0
- package/library/orm/Schema/MigrationRunner.ts +146 -0
- package/library/orm/Schema/Migrator.ts +73 -0
- package/library/orm/Schema/OrmOwnedStore.ts +123 -0
- package/library/orm/Schema/OwnedStoreAdmission.ts +752 -0
- package/library/orm/Schema/OwnedStoreCanonical.ts +273 -0
- package/library/orm/Schema/OwnedStoreCatalog.ts +1325 -0
- package/library/orm/Schema/SafeAdditiveSchema.ts +100 -0
- package/library/orm/Schema/SchemaAdmission.ts +62 -0
- package/library/orm/Schema/SchemaDiffer.ts +78 -0
- package/library/orm/Schema/formatSchemaDifferences.ts +62 -0
- package/library/orm/Schema/introspection.ts +52 -0
- package/library/orm/Schema/physicalColumnTypes.ts +37 -0
- package/library/orm/Schema/tableKey.ts +26 -0
- package/library/orm/Tracking/ChangeTracker.ts +448 -0
- package/library/orm/Tracking/EntityState.ts +13 -0
- package/library/orm/Transactions/OrmTransaction.ts +30 -0
- package/library/orm/Transactions/TransactionScopeCoordinator.ts +377 -0
- package/library/orm/errors.ts +190 -0
- package/library/orm/index.ts +162 -0
- package/library/orm/runtime.ts +14 -0
- package/library/redaction/index.ts +166 -0
- package/library/ui/UiProfile.ts +77 -0
- package/library/ui/compiled-v1.ts +131 -0
- package/library/ui/compiler/compileUiSurfaceV1.ts +717 -0
- package/library/ui/compiler/contracts-v1.ts +25 -0
- package/library/ui/compiler/index.ts +12 -0
- package/library/ui/compiler/openApiIndex.ts +246 -0
- package/library/ui/defineUiProfile.ts +340 -0
- package/library/ui/diagnostics-v1.ts +33 -0
- package/library/ui/index.ts +129 -0
- package/library/ui/profile-v1.ts +191 -0
- package/library/ui/serialization.ts +130 -0
- package/library/ui/uiProfileAuthoring.ts +289 -0
- package/library/ui/uiProfileAuthoringMetadata.ts +78 -0
- package/library/validation/MessageRegistry.ts +144 -0
- package/library/validation/README.md +62 -0
- package/library/validation/RuleCompiler.ts +183 -0
- package/library/validation/RuleEngine.ts +401 -0
- package/library/validation/SPEC.md +579 -0
- package/library/validation/ValidationError.ts +37 -0
- package/library/validation/ValidationResult.ts +63 -0
- package/library/validation/Validator.ts +101 -0
- package/library/validation/index.ts +16 -0
- package/library/validation/metadata.ts +69 -0
- package/library/validation/modelValidatorAdapter.ts +119 -0
- package/library/validation/types/ValidationCode.ts +69 -0
- package/library/validation/types/ValidatorOptions.ts +153 -0
- package/package.json +80 -0
|
@@ -0,0 +1,1325 @@
|
|
|
1
|
+
import { isDefinedOrmOwnedStoreV1, type OrmCatalogScopeV1, type OrmOwnedStoreDefinitionV1 } from "./OrmOwnedStore";
|
|
2
|
+
import { canonicalOwnedStoreScopeHashV1 } from "./OwnedStoreCanonical";
|
|
3
|
+
import { failure } from "../Providers/ormOwnedStoreRuntime";
|
|
4
|
+
import { types } from "node:util";
|
|
5
|
+
import type { CanonicalDefault } from "./introspection";
|
|
6
|
+
import type { CheckAst } from "./CheckExpression";
|
|
7
|
+
import { projectCheckAstIdentifiers } from "./CheckExpression";
|
|
8
|
+
import type { OrmExpectedSchema } from "./ExpectedSchema";
|
|
9
|
+
import { ExactSchemaVerifier } from "./ExactSchemaVerifier";
|
|
10
|
+
import type { IntrospectedTable } from "./introspection";
|
|
11
|
+
export type PgOidV1 = string;
|
|
12
|
+
export type PgIntV1 = string;
|
|
13
|
+
export type PgSignedIntV1 = string;
|
|
14
|
+
export type PgHashV1 = `sha256:${string}`;
|
|
15
|
+
export interface OwnedStoreCatalogContextV1 {
|
|
16
|
+
readonly maxIdentifierLength: bigint;
|
|
17
|
+
}
|
|
18
|
+
export interface OwnedCatalogClassV1 {
|
|
19
|
+
readonly oid: PgOidV1;
|
|
20
|
+
readonly schema: "pg_catalog" | "other";
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly kind: "pg_class" | "pg_type" | "pg_constraint" | "pg_proc" | "pg_rewrite" | "pg_namespace" | "pg_attrdef" | "pg_trigger" | "other";
|
|
23
|
+
}
|
|
24
|
+
export interface OwnedCatalogRelationV1 {
|
|
25
|
+
readonly oid: PgOidV1;
|
|
26
|
+
readonly namespaceOid: PgOidV1;
|
|
27
|
+
readonly schema: string;
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly kind: "ordinaryTable" | "partitionedTable" | "foreignTable" | "view" | "materializedView" | "sequence" | "index" | "partitionedIndex" | "toastTable" | "other";
|
|
30
|
+
readonly rawKind: string;
|
|
31
|
+
readonly persistence: "permanent" | "unlogged" | "temporary" | "other";
|
|
32
|
+
readonly isPartition: boolean;
|
|
33
|
+
readonly rowSecurity: boolean;
|
|
34
|
+
readonly forceRowSecurity: boolean;
|
|
35
|
+
readonly replicaIdentity: "default" | "nothing" | "full" | "index" | "other";
|
|
36
|
+
readonly tablespaceOid: PgOidV1 | "0";
|
|
37
|
+
readonly accessMethod: string | null;
|
|
38
|
+
readonly options: readonly string[];
|
|
39
|
+
readonly rowTypeOid: PgOidV1 | null;
|
|
40
|
+
readonly toastRelationOid: PgOidV1 | null;
|
|
41
|
+
}
|
|
42
|
+
export interface OwnedCatalogRowTypeV1 {
|
|
43
|
+
readonly oid: PgOidV1;
|
|
44
|
+
readonly relationOid: PgOidV1;
|
|
45
|
+
readonly schema: string;
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly kind: "composite" | "other";
|
|
48
|
+
readonly arrayTypeOid: PgOidV1;
|
|
49
|
+
}
|
|
50
|
+
export interface OwnedCatalogArrayTypeV1 {
|
|
51
|
+
readonly oid: PgOidV1;
|
|
52
|
+
readonly elementTypeOid: PgOidV1;
|
|
53
|
+
readonly relationOid: "0";
|
|
54
|
+
readonly arrayTypeOid: "0";
|
|
55
|
+
readonly schema: string;
|
|
56
|
+
readonly name: string;
|
|
57
|
+
readonly kind: "base" | "other";
|
|
58
|
+
readonly category: "array" | "other";
|
|
59
|
+
}
|
|
60
|
+
export interface OwnedCatalogColumnV1 {
|
|
61
|
+
readonly relationOid: PgOidV1;
|
|
62
|
+
readonly attnum: PgIntV1;
|
|
63
|
+
readonly name: string;
|
|
64
|
+
readonly dropped: boolean;
|
|
65
|
+
readonly local: boolean;
|
|
66
|
+
readonly inheritanceCount: PgIntV1;
|
|
67
|
+
readonly physicalType: string;
|
|
68
|
+
readonly typeOid: PgOidV1 | "0";
|
|
69
|
+
readonly notNull: boolean;
|
|
70
|
+
readonly default: CanonicalDefault;
|
|
71
|
+
readonly defaultObjectOid: PgOidV1 | null;
|
|
72
|
+
readonly generation: "none" | "identityByDefault" | "uuidDefault" | "other";
|
|
73
|
+
readonly identityCode: "" | "a" | "d" | "other";
|
|
74
|
+
readonly generatedCode: "" | "s" | "v" | "other";
|
|
75
|
+
readonly collationOid: PgOidV1 | "0";
|
|
76
|
+
readonly typeDefaultCollationOid: PgOidV1 | "0";
|
|
77
|
+
readonly storageCode: string;
|
|
78
|
+
readonly typeDefaultStorageCode: string;
|
|
79
|
+
readonly compressionCode: "" | "p" | "l" | "other";
|
|
80
|
+
}
|
|
81
|
+
export interface OwnedCatalogIndexV1 {
|
|
82
|
+
readonly indexRelationOid: PgOidV1;
|
|
83
|
+
readonly tableRelationOid: PgOidV1;
|
|
84
|
+
readonly name: string;
|
|
85
|
+
readonly method: string;
|
|
86
|
+
readonly unique: boolean;
|
|
87
|
+
readonly primary: boolean;
|
|
88
|
+
readonly exclusion: boolean;
|
|
89
|
+
readonly immediate: boolean;
|
|
90
|
+
readonly valid: boolean;
|
|
91
|
+
readonly ready: boolean;
|
|
92
|
+
readonly live: boolean;
|
|
93
|
+
readonly replicaIdentity: boolean;
|
|
94
|
+
readonly nullsNotDistinct: boolean;
|
|
95
|
+
readonly keyAttributeCount: PgIntV1;
|
|
96
|
+
readonly totalAttributeCount: PgIntV1;
|
|
97
|
+
readonly attributeNumbers: readonly PgSignedIntV1[];
|
|
98
|
+
readonly columnNames: readonly (string | null)[];
|
|
99
|
+
readonly collationOids: readonly (PgOidV1 | "0")[];
|
|
100
|
+
readonly opclassOids: readonly PgOidV1[];
|
|
101
|
+
readonly defaultOpclassOids: readonly PgOidV1[];
|
|
102
|
+
readonly options: readonly PgSignedIntV1[];
|
|
103
|
+
readonly expression: string | null;
|
|
104
|
+
readonly predicate: string | null;
|
|
105
|
+
readonly backingConstraintOid: PgOidV1 | null;
|
|
106
|
+
}
|
|
107
|
+
export interface OwnedCatalogConstraintV1 {
|
|
108
|
+
readonly oid: PgOidV1;
|
|
109
|
+
readonly relationOid: PgOidV1;
|
|
110
|
+
readonly referencedRelationOid: PgOidV1 | null;
|
|
111
|
+
readonly name: string;
|
|
112
|
+
readonly kind: "primaryKey" | "unique" | "foreignKey" | "check" | "exclusion" | "other";
|
|
113
|
+
readonly columns: readonly string[];
|
|
114
|
+
readonly referencedColumns: readonly string[];
|
|
115
|
+
readonly backingIndexOid: PgOidV1 | null;
|
|
116
|
+
readonly onDelete: string | null;
|
|
117
|
+
readonly onUpdate: string | null;
|
|
118
|
+
readonly match: string | null;
|
|
119
|
+
readonly deferrable: boolean;
|
|
120
|
+
readonly initiallyDeferred: boolean;
|
|
121
|
+
readonly validated: boolean;
|
|
122
|
+
readonly parentConstraintOid: PgOidV1 | null;
|
|
123
|
+
readonly inheritanceCount: PgIntV1;
|
|
124
|
+
readonly noInherit: boolean;
|
|
125
|
+
readonly deleteSetColumns: readonly string[];
|
|
126
|
+
readonly primaryForeignEqualityOperatorOids: readonly PgOidV1[];
|
|
127
|
+
readonly primaryPrimaryEqualityOperatorOids: readonly PgOidV1[];
|
|
128
|
+
readonly foreignForeignEqualityOperatorOids: readonly PgOidV1[];
|
|
129
|
+
readonly defaultEqualityOperatorOids: readonly PgOidV1[];
|
|
130
|
+
readonly checkExpression: CheckAst | null;
|
|
131
|
+
}
|
|
132
|
+
export interface OwnedCatalogTriggerV1 {
|
|
133
|
+
readonly oid: PgOidV1;
|
|
134
|
+
readonly relationOid: PgOidV1;
|
|
135
|
+
readonly name: string;
|
|
136
|
+
readonly internal: boolean;
|
|
137
|
+
readonly constraintOid: PgOidV1 | null;
|
|
138
|
+
readonly parentTriggerOid: PgOidV1 | null;
|
|
139
|
+
readonly enabled: "origin" | "always" | "replica" | "disabled" | "other";
|
|
140
|
+
readonly functionOid: PgOidV1;
|
|
141
|
+
readonly functionSchema: string;
|
|
142
|
+
readonly functionName: string;
|
|
143
|
+
readonly typeBits: PgIntV1;
|
|
144
|
+
}
|
|
145
|
+
export interface OwnedCatalogRuleV1 {
|
|
146
|
+
readonly oid: PgOidV1;
|
|
147
|
+
readonly relationOid: PgOidV1;
|
|
148
|
+
readonly name: string;
|
|
149
|
+
readonly event: string;
|
|
150
|
+
readonly enabled: string;
|
|
151
|
+
readonly instead: boolean;
|
|
152
|
+
}
|
|
153
|
+
export interface OwnedCatalogPolicyV1 {
|
|
154
|
+
readonly oid: PgOidV1;
|
|
155
|
+
readonly relationOid: PgOidV1;
|
|
156
|
+
readonly name: string;
|
|
157
|
+
readonly permissive: boolean;
|
|
158
|
+
readonly command: string;
|
|
159
|
+
readonly roles: readonly (PgOidV1 | "0")[];
|
|
160
|
+
readonly usingExpression: string | null;
|
|
161
|
+
readonly checkExpression: string | null;
|
|
162
|
+
}
|
|
163
|
+
export interface OwnedCatalogInheritanceV1 {
|
|
164
|
+
readonly childRelationOid: PgOidV1;
|
|
165
|
+
readonly parentRelationOid: PgOidV1;
|
|
166
|
+
readonly sequence: PgIntV1;
|
|
167
|
+
}
|
|
168
|
+
export interface OwnedCatalogDependencyV1 {
|
|
169
|
+
readonly dependentClassOid: PgOidV1;
|
|
170
|
+
readonly dependentOid: PgOidV1;
|
|
171
|
+
readonly dependentSubId: PgIntV1;
|
|
172
|
+
readonly referencedClassOid: PgOidV1;
|
|
173
|
+
readonly referencedOid: PgOidV1;
|
|
174
|
+
readonly referencedSubId: PgIntV1;
|
|
175
|
+
readonly kind: "normal" | "automatic" | "internal" | "partitionPrimary" | "partitionSecondary" | "extension" | "other";
|
|
176
|
+
}
|
|
177
|
+
export interface OwnedCatalogSequenceV1 {
|
|
178
|
+
readonly relationOid: PgOidV1;
|
|
179
|
+
readonly type: "bigint" | "integer" | "smallint" | "other";
|
|
180
|
+
readonly start: PgSignedIntV1;
|
|
181
|
+
readonly increment: PgSignedIntV1;
|
|
182
|
+
readonly minimum: PgSignedIntV1;
|
|
183
|
+
readonly maximum: PgSignedIntV1;
|
|
184
|
+
readonly cache: PgIntV1;
|
|
185
|
+
readonly cycle: boolean;
|
|
186
|
+
}
|
|
187
|
+
export interface OwnedCatalogToastClosureV1 {
|
|
188
|
+
readonly ownerTableOid: PgOidV1;
|
|
189
|
+
readonly relation: OwnedCatalogRelationV1;
|
|
190
|
+
readonly columns: readonly OwnedCatalogColumnV1[];
|
|
191
|
+
readonly indexes: readonly OwnedCatalogIndexV1[];
|
|
192
|
+
readonly indexRelations: readonly OwnedCatalogRelationV1[];
|
|
193
|
+
readonly dependencies: readonly OwnedCatalogDependencyV1[];
|
|
194
|
+
}
|
|
195
|
+
export interface OwnedStoreRegistryShapeV1 {
|
|
196
|
+
readonly catalogClasses: readonly OwnedCatalogClassV1[];
|
|
197
|
+
readonly relation: OwnedCatalogRelationV1;
|
|
198
|
+
readonly rowType: OwnedCatalogRowTypeV1;
|
|
199
|
+
readonly arrayType: OwnedCatalogArrayTypeV1;
|
|
200
|
+
readonly columns: readonly OwnedCatalogColumnV1[];
|
|
201
|
+
readonly indexes: readonly OwnedCatalogIndexV1[];
|
|
202
|
+
readonly indexRelations: readonly OwnedCatalogRelationV1[];
|
|
203
|
+
readonly constraints: readonly OwnedCatalogConstraintV1[];
|
|
204
|
+
readonly triggers: readonly OwnedCatalogTriggerV1[];
|
|
205
|
+
readonly rules: readonly OwnedCatalogRuleV1[];
|
|
206
|
+
readonly policies: readonly OwnedCatalogPolicyV1[];
|
|
207
|
+
readonly inheritance: readonly OwnedCatalogInheritanceV1[];
|
|
208
|
+
readonly dependencies: readonly OwnedCatalogDependencyV1[];
|
|
209
|
+
readonly sequences: readonly OwnedCatalogSequenceV1[];
|
|
210
|
+
readonly toast: OwnedCatalogToastClosureV1 | null;
|
|
211
|
+
}
|
|
212
|
+
export interface OwnedStoreRegistryRowV1 {
|
|
213
|
+
readonly storeKey: string;
|
|
214
|
+
readonly contract: "bazis.orm-owned-store/v1";
|
|
215
|
+
readonly formatVersion: string;
|
|
216
|
+
readonly ownedSchema: string;
|
|
217
|
+
readonly tablePrefix: string;
|
|
218
|
+
readonly ownedScopeHash: PgHashV1;
|
|
219
|
+
readonly modelHash: PgHashV1;
|
|
220
|
+
readonly createdAtEpochMicroseconds: string;
|
|
221
|
+
}
|
|
222
|
+
export interface OwnedStoreRegistrySnapshotV1 {
|
|
223
|
+
readonly contract: "bazis.orm-owned-store-registry-snapshot/v1";
|
|
224
|
+
readonly publicSchemaExists: boolean;
|
|
225
|
+
readonly state: {
|
|
226
|
+
readonly kind: "absent";
|
|
227
|
+
} | {
|
|
228
|
+
readonly kind: "present";
|
|
229
|
+
readonly shape: OwnedStoreRegistryShapeV1;
|
|
230
|
+
readonly rows: readonly OwnedStoreRegistryRowV1[];
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
export interface OwnedStoreCatalogSnapshotV1 {
|
|
234
|
+
readonly contract: "bazis.orm-owned-store-catalog-snapshot/v1";
|
|
235
|
+
readonly requestedScopes: readonly OrmCatalogScopeV1[];
|
|
236
|
+
readonly existingSchemas: readonly string[];
|
|
237
|
+
readonly catalogClasses: readonly OwnedCatalogClassV1[];
|
|
238
|
+
readonly relations: readonly OwnedCatalogRelationV1[];
|
|
239
|
+
readonly rowTypes: readonly OwnedCatalogRowTypeV1[];
|
|
240
|
+
readonly arrayTypes: readonly OwnedCatalogArrayTypeV1[];
|
|
241
|
+
readonly columns: readonly OwnedCatalogColumnV1[];
|
|
242
|
+
readonly indexes: readonly OwnedCatalogIndexV1[];
|
|
243
|
+
readonly constraints: readonly OwnedCatalogConstraintV1[];
|
|
244
|
+
readonly triggers: readonly OwnedCatalogTriggerV1[];
|
|
245
|
+
readonly rules: readonly OwnedCatalogRuleV1[];
|
|
246
|
+
readonly policies: readonly OwnedCatalogPolicyV1[];
|
|
247
|
+
readonly inheritance: readonly OwnedCatalogInheritanceV1[];
|
|
248
|
+
readonly dependencies: readonly OwnedCatalogDependencyV1[];
|
|
249
|
+
readonly sequences: readonly OwnedCatalogSequenceV1[];
|
|
250
|
+
}
|
|
251
|
+
const hash = /^sha256:[0-9a-f]{64}$/u, integer = /^(0|[1-9][0-9]*)$/u, signed = /^(0|[1-9][0-9]*|-[1-9][0-9]*)$/u;
|
|
252
|
+
function bad(): never {
|
|
253
|
+
throw failure("ORM_OWNED_STORE_DRIFT");
|
|
254
|
+
}
|
|
255
|
+
function record(v: unknown, keys: readonly string[], required = keys): Record<string, unknown> {
|
|
256
|
+
if (v === null || typeof v !== "object" || types.isProxy(v) || Array.isArray(v))
|
|
257
|
+
bad();
|
|
258
|
+
const proto = Object.getPrototypeOf(v);
|
|
259
|
+
if (proto !== Object.prototype && proto !== null || Object.getOwnPropertySymbols(v).length)
|
|
260
|
+
bad();
|
|
261
|
+
const d = Object.getOwnPropertyDescriptors(v);
|
|
262
|
+
if (Object.keys(d).some(k => !keys.includes(k) || !("value" in d[k]!)) || required.some(k => !Object.hasOwn(d, k)))
|
|
263
|
+
bad();
|
|
264
|
+
return Object.fromEntries(Object.entries(d).map(([k, x]) => [k, (x as PropertyDescriptor & {
|
|
265
|
+
value: unknown;
|
|
266
|
+
}).value]));
|
|
267
|
+
}
|
|
268
|
+
function dense<T>(v: unknown, parse: (x: unknown) => T, max: number): readonly T[] {
|
|
269
|
+
if (v === null || typeof v !== "object" || types.isProxy(v) || !Array.isArray(v) || Object.getPrototypeOf(v) !== Array.prototype || Object.getOwnPropertySymbols(v).length)
|
|
270
|
+
bad();
|
|
271
|
+
const length = Object.getOwnPropertyDescriptor(v, "length");
|
|
272
|
+
if (!length || !("value" in length) || typeof length.value !== "number" || length.value > max)
|
|
273
|
+
bad();
|
|
274
|
+
const d = Object.getOwnPropertyDescriptors(v);
|
|
275
|
+
for (const key of Object.keys(d)) {
|
|
276
|
+
if (key === "length")
|
|
277
|
+
continue;
|
|
278
|
+
if (!/^(0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length.value || !("value" in d[key]!))
|
|
279
|
+
bad();
|
|
280
|
+
}
|
|
281
|
+
const out: T[] = [];
|
|
282
|
+
for (let i = 0; i < length.value; i++) {
|
|
283
|
+
const x = d[String(i)];
|
|
284
|
+
if (!x || !("value" in x))
|
|
285
|
+
bad();
|
|
286
|
+
out.push(parse(x.value));
|
|
287
|
+
}
|
|
288
|
+
return Object.freeze(out);
|
|
289
|
+
}
|
|
290
|
+
function text(v: unknown, min: number, max: number): string {
|
|
291
|
+
if (typeof v !== "string" || Buffer.byteLength(v, "utf8") < min || Buffer.byteLength(v, "utf8") > max)
|
|
292
|
+
bad();
|
|
293
|
+
for (let i = 0; i < v.length; i++) {
|
|
294
|
+
const c = v.charCodeAt(i);
|
|
295
|
+
if (c <= 0x1f || (c >= 0x7f && c <= 0x9f))
|
|
296
|
+
bad();
|
|
297
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
298
|
+
if (i + 1 >= v.length)
|
|
299
|
+
bad();
|
|
300
|
+
const next = v.charCodeAt(i + 1);
|
|
301
|
+
if (next < 0xdc00 || next > 0xdfff)
|
|
302
|
+
bad();
|
|
303
|
+
i++;
|
|
304
|
+
}
|
|
305
|
+
else if (c >= 0xdc00 && c <= 0xdfff)
|
|
306
|
+
bad();
|
|
307
|
+
}
|
|
308
|
+
return v;
|
|
309
|
+
}
|
|
310
|
+
function natural(v: unknown, positive: boolean, max: bigint): string {
|
|
311
|
+
const s = text(v, 1, max.toString().length);
|
|
312
|
+
if (!integer.test(s) || (positive && s === "0") || BigInt(s) > max)
|
|
313
|
+
bad();
|
|
314
|
+
return s;
|
|
315
|
+
}
|
|
316
|
+
function oid(v: unknown): PgOidV1 {
|
|
317
|
+
return natural(v, true, 4294967295n);
|
|
318
|
+
}
|
|
319
|
+
function rawText(v: unknown): string {
|
|
320
|
+
if (typeof v !== "string")
|
|
321
|
+
bad();
|
|
322
|
+
for (let i = 0; i < v.length; i++) {
|
|
323
|
+
const c = v.charCodeAt(i);
|
|
324
|
+
if (c <= 0x1f || (c >= 0x7f && c <= 0x9f))
|
|
325
|
+
bad();
|
|
326
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
327
|
+
if (i + 1 >= v.length)
|
|
328
|
+
bad();
|
|
329
|
+
const next = v.charCodeAt(i + 1);
|
|
330
|
+
if (next < 0xdc00 || next > 0xdfff)
|
|
331
|
+
bad();
|
|
332
|
+
i++;
|
|
333
|
+
}
|
|
334
|
+
else if (c >= 0xdc00 && c <= 0xdfff)
|
|
335
|
+
bad();
|
|
336
|
+
}
|
|
337
|
+
return v;
|
|
338
|
+
}
|
|
339
|
+
function dataText(v: unknown): string {
|
|
340
|
+
if (typeof v !== "string" || Buffer.byteLength(v, "utf8") > 4 * 1024 * 1024)
|
|
341
|
+
bad();
|
|
342
|
+
for (let i = 0; i < v.length; i++) {
|
|
343
|
+
const c = v.charCodeAt(i);
|
|
344
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
345
|
+
if (i + 1 >= v.length)
|
|
346
|
+
bad();
|
|
347
|
+
const next = v.charCodeAt(i + 1);
|
|
348
|
+
if (!Number.isInteger(next) || next < 0xdc00 || next > 0xdfff)
|
|
349
|
+
bad();
|
|
350
|
+
i++;
|
|
351
|
+
}
|
|
352
|
+
else if (c >= 0xdc00 && c <= 0xdfff)
|
|
353
|
+
bad();
|
|
354
|
+
}
|
|
355
|
+
return v;
|
|
356
|
+
}
|
|
357
|
+
function literal<T extends string>(v: unknown, values: readonly T[]): T {
|
|
358
|
+
return typeof v === "string" && values.includes(v as T) ? v as T : bad();
|
|
359
|
+
}
|
|
360
|
+
function nullableOid(v: unknown): PgOidV1 | null {
|
|
361
|
+
return v === null ? null : oid(v);
|
|
362
|
+
}
|
|
363
|
+
function oidOrZero(v: unknown): PgOidV1 | "0" {
|
|
364
|
+
return v === "0" ? "0" : oid(v);
|
|
365
|
+
}
|
|
366
|
+
function parseOwnedStoreCatalogContextV1(v: unknown): OwnedStoreCatalogContextV1 {
|
|
367
|
+
const r = record(v, ["maxIdentifierLength"]);
|
|
368
|
+
if (typeof r.maxIdentifierLength !== "bigint")
|
|
369
|
+
bad();
|
|
370
|
+
if (r.maxIdentifierLength < 63n)
|
|
371
|
+
throw failure("ORM_OWNED_STORE_PROVIDER_UNSUPPORTED");
|
|
372
|
+
return Object.freeze({ maxIdentifierLength: r.maxIdentifierLength });
|
|
373
|
+
}
|
|
374
|
+
function serverIdentifier(v: unknown, max: bigint): string {
|
|
375
|
+
if (typeof v !== "string")
|
|
376
|
+
bad();
|
|
377
|
+
const bytes = Buffer.byteLength(v, "utf8");
|
|
378
|
+
if (bytes < 1 || BigInt(bytes) > max)
|
|
379
|
+
bad();
|
|
380
|
+
for (let i = 0; i < v.length; i++) {
|
|
381
|
+
const c = v.charCodeAt(i);
|
|
382
|
+
if (c <= 0x1f || (c >= 0x7f && c <= 0x9f))
|
|
383
|
+
bad();
|
|
384
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
385
|
+
if (i + 1 >= v.length)
|
|
386
|
+
bad();
|
|
387
|
+
const next = v.charCodeAt(i + 1);
|
|
388
|
+
if (next < 0xdc00 || next > 0xdfff)
|
|
389
|
+
bad();
|
|
390
|
+
i++;
|
|
391
|
+
}
|
|
392
|
+
else if (c >= 0xdc00 && c <= 0xdfff)
|
|
393
|
+
bad();
|
|
394
|
+
}
|
|
395
|
+
return v;
|
|
396
|
+
}
|
|
397
|
+
export function parseOwnedCatalogClassV1(v: unknown): OwnedCatalogClassV1 {
|
|
398
|
+
const r = record(v, ["oid", "schema", "name", "kind"]);
|
|
399
|
+
return Object.freeze({
|
|
400
|
+
oid: oid(r.oid), schema: literal(r.schema, ["pg_catalog", "other"]), name: text(r.name, 1, 63), kind: literal(r.kind, [
|
|
401
|
+
"pg_class", "pg_type", "pg_constraint", "pg_proc", "pg_rewrite", "pg_namespace", "pg_attrdef", "pg_trigger", "other"
|
|
402
|
+
])
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
export function parseOwnedCatalogRelationV1(v: unknown, context: OwnedStoreCatalogContextV1): OwnedCatalogRelationV1 {
|
|
406
|
+
const c = parseOwnedStoreCatalogContextV1(context), r = record(v, [
|
|
407
|
+
"oid", "namespaceOid", "schema", "name", "kind", "rawKind", "persistence", "isPartition", "rowSecurity", "forceRowSecurity", "replicaIdentity", "tablespaceOid", "accessMethod", "options", "rowTypeOid", "toastRelationOid"
|
|
408
|
+
]), kind = literal(r.kind, [
|
|
409
|
+
"ordinaryTable", "partitionedTable", "foreignTable", "view", "materializedView", "sequence", "index", "partitionedIndex", "toastTable", "other"
|
|
410
|
+
]);
|
|
411
|
+
return Object.freeze({
|
|
412
|
+
oid: oid(r.oid), namespaceOid: oid(r.namespaceOid), schema: text(r.schema, 1, 63), name: kind === "sequence" ? serverIdentifier(r.name, c.maxIdentifierLength) : text(r.name, 1, 63), kind, rawKind: rawText(r.rawKind), persistence: literal(r.persistence, ["permanent", "unlogged", "temporary", "other"]), isPartition: typeof r.isPartition === "boolean" ? r.isPartition : bad(), rowSecurity: typeof r.rowSecurity === "boolean" ? r.rowSecurity : bad(), forceRowSecurity: typeof r.forceRowSecurity === "boolean" ? r.forceRowSecurity : bad(), replicaIdentity: literal(r.replicaIdentity, ["default", "nothing", "full", "index", "other"]), tablespaceOid: oidOrZero(r.tablespaceOid), accessMethod: r.accessMethod === null ? null : rawText(r.accessMethod), options: dense(r.options, rawText, 65536), rowTypeOid: nullableOid(r.rowTypeOid), toastRelationOid: nullableOid(r.toastRelationOid)
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
export function parseOwnedCatalogRowTypeV1(v: unknown): OwnedCatalogRowTypeV1 {
|
|
416
|
+
const r = record(v, ["oid", "relationOid", "schema", "name", "kind", "arrayTypeOid"]);
|
|
417
|
+
return Object.freeze({
|
|
418
|
+
oid: oid(r.oid), relationOid: oid(r.relationOid), schema: text(r.schema, 1, 63), name: text(r.name, 1, 63), kind: literal(r.kind, ["composite", "other"]), arrayTypeOid: oid(r.arrayTypeOid)
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
export function parseOwnedCatalogArrayTypeV1(v: unknown, context: OwnedStoreCatalogContextV1): OwnedCatalogArrayTypeV1 {
|
|
422
|
+
const c = parseOwnedStoreCatalogContextV1(context), r = record(v, ["oid", "elementTypeOid", "relationOid", "arrayTypeOid", "schema", "name", "kind", "category"]);
|
|
423
|
+
if (r.relationOid !== "0" || r.arrayTypeOid !== "0")
|
|
424
|
+
bad();
|
|
425
|
+
return Object.freeze({
|
|
426
|
+
oid: oid(r.oid), elementTypeOid: oid(r.elementTypeOid), relationOid: "0", arrayTypeOid: "0", schema: text(r.schema, 1, 63), name: serverIdentifier(r.name, c.maxIdentifierLength), kind: literal(r.kind, ["base", "other"]), category: literal(r.category, ["array", "other"])
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
export function parseCanonicalDefaultV1(v: unknown): CanonicalDefault {
|
|
430
|
+
const r = record(v, ["kind", "value"], ["kind"]);
|
|
431
|
+
const kind = literal(r.kind, ["none", "null", "currentTimestamp", "uuidV4", "boolean", "number", "string"]);
|
|
432
|
+
switch (kind) {
|
|
433
|
+
case "none":
|
|
434
|
+
case "null":
|
|
435
|
+
case "currentTimestamp":
|
|
436
|
+
case "uuidV4":
|
|
437
|
+
if (Object.hasOwn(r, "value"))
|
|
438
|
+
bad();
|
|
439
|
+
return Object.freeze({ kind });
|
|
440
|
+
case "boolean":
|
|
441
|
+
if (typeof r.value !== "boolean")
|
|
442
|
+
bad();
|
|
443
|
+
return Object.freeze({ kind, value: r.value });
|
|
444
|
+
case "number":
|
|
445
|
+
if (typeof r.value !== "number" || !Number.isFinite(r.value))
|
|
446
|
+
bad();
|
|
447
|
+
return Object.freeze({ kind, value: Object.is(r.value, -0) ? 0 : r.value });
|
|
448
|
+
case "string": return Object.freeze({ kind, value: dataText(r.value) });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function small(v: unknown, positive = false): string {
|
|
452
|
+
return natural(v, positive, 32767n);
|
|
453
|
+
}
|
|
454
|
+
function signedSmall(v: unknown): string {
|
|
455
|
+
const x = text(v, 1, 6);
|
|
456
|
+
if (!signed.test(x) || BigInt(x) < -32768n || BigInt(x) > 32767n)
|
|
457
|
+
bad();
|
|
458
|
+
return x;
|
|
459
|
+
}
|
|
460
|
+
function signedInt64(v: unknown): PgSignedIntV1 {
|
|
461
|
+
if (typeof v !== "string" || v.length > 20 || !signed.test(v))
|
|
462
|
+
bad();
|
|
463
|
+
const x = BigInt(v);
|
|
464
|
+
if (x < -9223372036854775808n || x > 9223372036854775807n)
|
|
465
|
+
bad();
|
|
466
|
+
return v;
|
|
467
|
+
}
|
|
468
|
+
export function parseOwnedCatalogColumnV1(v: unknown): OwnedCatalogColumnV1 {
|
|
469
|
+
const r = record(v, [
|
|
470
|
+
"relationOid", "attnum", "name", "dropped", "local", "inheritanceCount", "physicalType", "typeOid", "notNull", "default", "defaultObjectOid", "generation", "identityCode", "generatedCode", "collationOid", "typeDefaultCollationOid", "storageCode", "typeDefaultStorageCode", "compressionCode"
|
|
471
|
+
]);
|
|
472
|
+
return Object.freeze({
|
|
473
|
+
relationOid: oid(r.relationOid), attnum: small(r.attnum, true), name: text(r.name, 1, 63), dropped: typeof r.dropped === "boolean" ? r.dropped : bad(), local: typeof r.local === "boolean" ? r.local : bad(), inheritanceCount: small(r.inheritanceCount), physicalType: rawText(r.physicalType), typeOid: oidOrZero(r.typeOid), notNull: typeof r.notNull === "boolean" ? r.notNull : bad(), default: parseCanonicalDefaultV1(r.default), defaultObjectOid: nullableOid(r.defaultObjectOid), generation: literal(r.generation, ["none", "identityByDefault", "uuidDefault", "other"]), identityCode: literal(r.identityCode, ["", "a", "d", "other"]), generatedCode: literal(r.generatedCode, ["", "s", "v", "other"]), collationOid: oidOrZero(r.collationOid), typeDefaultCollationOid: oidOrZero(r.typeDefaultCollationOid), storageCode: rawText(r.storageCode), typeDefaultStorageCode: rawText(r.typeDefaultStorageCode), compressionCode: literal(r.compressionCode, ["", "p", "l", "other"])
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
export function parseOwnedCatalogIndexV1(v: unknown): OwnedCatalogIndexV1 {
|
|
477
|
+
const r = record(v, [
|
|
478
|
+
"indexRelationOid", "tableRelationOid", "name", "method", "unique", "primary", "exclusion", "immediate", "valid", "ready", "live", "replicaIdentity", "nullsNotDistinct", "keyAttributeCount", "totalAttributeCount", "attributeNumbers", "columnNames", "collationOids", "opclassOids", "defaultOpclassOids", "options", "expression", "predicate", "backingConstraintOid"
|
|
479
|
+
]);
|
|
480
|
+
const key = small(r.keyAttributeCount, true), total = small(r.totalAttributeCount, true);
|
|
481
|
+
if (BigInt(key) > BigInt(total))
|
|
482
|
+
bad();
|
|
483
|
+
const keyMax = Number(key), totalMax = Number(total);
|
|
484
|
+
const attrs = dense(r.attributeNumbers, signedSmall, totalMax), names = dense(r.columnNames, x => x === null ? null : text(x, 1, 63), totalMax), coll = dense(r.collationOids, oidOrZero, keyMax), op = dense(r.opclassOids, oid, keyMax), def = dense(r.defaultOpclassOids, oid, keyMax), opts = dense(r.options, signedSmall, keyMax);
|
|
485
|
+
if (attrs.length !== totalMax || names.length !== totalMax || coll.length !== keyMax || op.length !== keyMax || def.length !== keyMax || opts.length !== keyMax)
|
|
486
|
+
bad();
|
|
487
|
+
const bool = (x: unknown) => typeof x === "boolean" ? x : bad();
|
|
488
|
+
return Object.freeze({
|
|
489
|
+
indexRelationOid: oid(r.indexRelationOid), tableRelationOid: oid(r.tableRelationOid), name: text(r.name, 1, 63), method: rawText(r.method), unique: bool(r.unique), primary: bool(r.primary), exclusion: bool(r.exclusion), immediate: bool(r.immediate), valid: bool(r.valid), ready: bool(r.ready), live: bool(r.live), replicaIdentity: bool(r.replicaIdentity), nullsNotDistinct: bool(r.nullsNotDistinct), keyAttributeCount: key, totalAttributeCount: total, attributeNumbers: attrs, columnNames: names, collationOids: coll, opclassOids: op, defaultOpclassOids: def, options: opts, expression: r.expression === null ? null : dataText(r.expression), predicate: r.predicate === null ? null : dataText(r.predicate), backingConstraintOid: nullableOid(r.backingConstraintOid)
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
export function parseOwnedCatalogTriggerV1(v: unknown): OwnedCatalogTriggerV1 {
|
|
493
|
+
const r = record(v, [
|
|
494
|
+
"oid", "relationOid", "name", "internal", "constraintOid", "parentTriggerOid", "enabled", "functionOid", "functionSchema", "functionName", "typeBits"
|
|
495
|
+
]);
|
|
496
|
+
return Object.freeze({
|
|
497
|
+
oid: oid(r.oid), relationOid: oid(r.relationOid), name: text(r.name, 1, 63), internal: typeof r.internal === "boolean" ? r.internal : bad(), constraintOid: nullableOid(r.constraintOid), parentTriggerOid: nullableOid(r.parentTriggerOid), enabled: literal(r.enabled, ["origin", "always", "replica", "disabled", "other"]), functionOid: oid(r.functionOid), functionSchema: text(r.functionSchema, 1, 63), functionName: text(r.functionName, 1, 63), typeBits: small(r.typeBits, true)
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
export function parseOwnedCatalogRuleV1(v: unknown): OwnedCatalogRuleV1 {
|
|
501
|
+
const r = record(v, ["oid", "relationOid", "name", "event", "enabled", "instead"]);
|
|
502
|
+
return Object.freeze({
|
|
503
|
+
oid: oid(r.oid), relationOid: oid(r.relationOid), name: text(r.name, 1, 63), event: rawText(r.event), enabled: rawText(r.enabled), instead: typeof r.instead === "boolean" ? r.instead : bad()
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
export function parseOwnedCatalogPolicyV1(v: unknown): OwnedCatalogPolicyV1 {
|
|
507
|
+
const r = record(v, ["oid", "relationOid", "name", "permissive", "command", "roles", "usingExpression", "checkExpression"]);
|
|
508
|
+
return Object.freeze({
|
|
509
|
+
oid: oid(r.oid), relationOid: oid(r.relationOid), name: text(r.name, 1, 63), permissive: typeof r.permissive === "boolean" ? r.permissive : bad(), command: rawText(r.command), roles: dense(r.roles, oidOrZero, 65536), usingExpression: r.usingExpression === null ? null : dataText(r.usingExpression), checkExpression: r.checkExpression === null ? null : dataText(r.checkExpression)
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function checkScalar(v: unknown): null | boolean | number | string {
|
|
513
|
+
if (v === null || typeof v === "boolean")
|
|
514
|
+
return v;
|
|
515
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
516
|
+
return Object.is(v, -0) ? 0 : v;
|
|
517
|
+
if (typeof v === "string")
|
|
518
|
+
return dataText(v);
|
|
519
|
+
bad();
|
|
520
|
+
}
|
|
521
|
+
function checkKeys(r: Record<string, unknown>, keys: readonly string[]): void {
|
|
522
|
+
if (Object.keys(r).length !== keys.length || keys.some(key => !Object.hasOwn(r, key)))
|
|
523
|
+
bad();
|
|
524
|
+
}
|
|
525
|
+
export function parseCheckAstV1(v: unknown): CheckAst {
|
|
526
|
+
let count = 0;
|
|
527
|
+
const active = new WeakSet<object>();
|
|
528
|
+
const visit = (value: unknown, depth: number): CheckAst => {
|
|
529
|
+
if (depth > 64 || value === null || typeof value !== "object" || types.isProxy(value) || Array.isArray(value))
|
|
530
|
+
bad();
|
|
531
|
+
if (active.has(value))
|
|
532
|
+
bad();
|
|
533
|
+
active.add(value);
|
|
534
|
+
try {
|
|
535
|
+
const r = record(value, ["kind", "op", "left", "right", "values", "not", "inner"], ["kind"]), kind = literal(r.kind, ["compare", "in", "null", "and", "or", "not"]);
|
|
536
|
+
count++;
|
|
537
|
+
if (count > 4096)
|
|
538
|
+
bad();
|
|
539
|
+
switch (kind) {
|
|
540
|
+
case "compare": {
|
|
541
|
+
checkKeys(r, ["kind", "op", "left", "right"]);
|
|
542
|
+
const right = typeof r.right === "string" && r.right.startsWith("\0") ? `\0${text(r.right.slice(1), 1, 63)}` : checkScalar(r.right);
|
|
543
|
+
return Object.freeze({ kind, op: literal(r.op, ["=", "<>", ">", ">=", "<", "<="]), left: text(r.left, 1, 63), right });
|
|
544
|
+
}
|
|
545
|
+
case "in": {
|
|
546
|
+
checkKeys(r, ["kind", "left", "values"]);
|
|
547
|
+
const values = dense(r.values, checkScalar, 100);
|
|
548
|
+
if (!values.length)
|
|
549
|
+
bad();
|
|
550
|
+
return Object.freeze({ kind, left: text(r.left, 1, 63), values });
|
|
551
|
+
}
|
|
552
|
+
case "null":
|
|
553
|
+
checkKeys(r, ["kind", "left", "not"]);
|
|
554
|
+
return Object.freeze({ kind, left: text(r.left, 1, 63), not: typeof r.not === "boolean" ? r.not : bad() });
|
|
555
|
+
case "and":
|
|
556
|
+
case "or":
|
|
557
|
+
checkKeys(r, ["kind", "left", "right"]);
|
|
558
|
+
return Object.freeze({ kind, left: visit(r.left, depth + 1), right: visit(r.right, depth + 1) });
|
|
559
|
+
case "not":
|
|
560
|
+
checkKeys(r, ["kind", "inner"]);
|
|
561
|
+
return Object.freeze({ kind, inner: visit(r.inner, depth + 1) });
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
finally {
|
|
565
|
+
active.delete(value);
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
return visit(v, 1);
|
|
569
|
+
}
|
|
570
|
+
export function parseOwnedCatalogConstraintV1(v: unknown, context: OwnedStoreCatalogContextV1): OwnedCatalogConstraintV1 {
|
|
571
|
+
const c = parseOwnedStoreCatalogContextV1(context), r = record(v, [
|
|
572
|
+
"oid", "relationOid", "referencedRelationOid", "name", "kind", "columns", "referencedColumns", "backingIndexOid", "onDelete", "onUpdate", "match", "deferrable", "initiallyDeferred", "validated", "parentConstraintOid", "inheritanceCount", "noInherit", "deleteSetColumns", "primaryForeignEqualityOperatorOids", "primaryPrimaryEqualityOperatorOids", "foreignForeignEqualityOperatorOids", "defaultEqualityOperatorOids", "checkExpression"
|
|
573
|
+
]);
|
|
574
|
+
const names = (x: unknown) => dense(x, y => serverIdentifier(y, c.maxIdentifierLength), 65536);
|
|
575
|
+
return Object.freeze({
|
|
576
|
+
oid: oid(r.oid), relationOid: oid(r.relationOid), referencedRelationOid: nullableOid(r.referencedRelationOid), name: serverIdentifier(r.name, c.maxIdentifierLength), kind: literal(r.kind, ["primaryKey", "unique", "foreignKey", "check", "exclusion", "other"]), columns: names(r.columns), referencedColumns: names(r.referencedColumns), backingIndexOid: nullableOid(r.backingIndexOid), onDelete: r.onDelete === null ? null : rawText(r.onDelete), onUpdate: r.onUpdate === null ? null : rawText(r.onUpdate), match: r.match === null ? null : rawText(r.match), deferrable: typeof r.deferrable === "boolean" ? r.deferrable : bad(), initiallyDeferred: typeof r.initiallyDeferred === "boolean" ? r.initiallyDeferred : bad(), validated: typeof r.validated === "boolean" ? r.validated : bad(), parentConstraintOid: nullableOid(r.parentConstraintOid), inheritanceCount: small(r.inheritanceCount), noInherit: typeof r.noInherit === "boolean" ? r.noInherit : bad(), deleteSetColumns: names(r.deleteSetColumns), primaryForeignEqualityOperatorOids: dense(r.primaryForeignEqualityOperatorOids, oid, 65536), primaryPrimaryEqualityOperatorOids: dense(r.primaryPrimaryEqualityOperatorOids, oid, 65536), foreignForeignEqualityOperatorOids: dense(r.foreignForeignEqualityOperatorOids, oid, 65536), defaultEqualityOperatorOids: dense(r.defaultEqualityOperatorOids, oid, 65536), checkExpression: r.checkExpression === null ? null : parseCheckAstV1(r.checkExpression)
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
export function parseOwnedCatalogInheritanceV1(v: unknown): OwnedCatalogInheritanceV1 {
|
|
580
|
+
const r = record(v, ["childRelationOid", "parentRelationOid", "sequence"]);
|
|
581
|
+
return Object.freeze({
|
|
582
|
+
childRelationOid: oid(r.childRelationOid), parentRelationOid: oid(r.parentRelationOid), sequence: natural(r.sequence, true, 2147483647n)
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
export function parseOwnedCatalogDependencyV1(v: unknown): OwnedCatalogDependencyV1 {
|
|
586
|
+
const r = record(v, [
|
|
587
|
+
"dependentClassOid", "dependentOid", "dependentSubId", "referencedClassOid", "referencedOid", "referencedSubId", "kind"
|
|
588
|
+
]);
|
|
589
|
+
return Object.freeze({
|
|
590
|
+
dependentClassOid: oid(r.dependentClassOid), dependentOid: oid(r.dependentOid), dependentSubId: natural(r.dependentSubId, false, 2147483647n), referencedClassOid: oid(r.referencedClassOid), referencedOid: oid(r.referencedOid), referencedSubId: natural(r.referencedSubId, false, 2147483647n), kind: literal(r.kind, ["normal", "automatic", "internal", "partitionPrimary", "partitionSecondary", "extension", "other"])
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
export function parseOwnedCatalogSequenceV1(v: unknown): OwnedCatalogSequenceV1 {
|
|
594
|
+
const r = record(v, ["relationOid", "type", "start", "increment", "minimum", "maximum", "cache", "cycle"]);
|
|
595
|
+
return Object.freeze({
|
|
596
|
+
relationOid: oid(r.relationOid), type: literal(r.type, ["bigint", "integer", "smallint", "other"]), start: signedInt64(r.start), increment: signedInt64(r.increment), minimum: signedInt64(r.minimum), maximum: signedInt64(r.maximum), cache: natural(r.cache, true, 9223372036854775807n), cycle: typeof r.cycle === "boolean" ? r.cycle : bad()
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
function row(v: unknown): OwnedStoreRegistryRowV1 {
|
|
600
|
+
const r = record(v, [
|
|
601
|
+
"storeKey", "contract", "formatVersion", "ownedSchema", "tablePrefix", "ownedScopeHash", "modelHash", "createdAtEpochMicroseconds"
|
|
602
|
+
]);
|
|
603
|
+
const out = {
|
|
604
|
+
storeKey: text(r.storeKey, 1, 128), contract: r.contract, formatVersion: natural(r.formatVersion, true, BigInt(Number.MAX_SAFE_INTEGER)), ownedSchema: text(r.ownedSchema, 1, 63), tablePrefix: text(r.tablePrefix, 1, 63), ownedScopeHash: text(r.ownedScopeHash, 1, 71), modelHash: text(r.modelHash, 1, 71), createdAtEpochMicroseconds: natural(r.createdAtEpochMicroseconds, false, 8640000000000000000n)
|
|
605
|
+
};
|
|
606
|
+
if (out.contract !== "bazis.orm-owned-store/v1" || !hash.test(out.ownedScopeHash) || !hash.test(out.modelHash))
|
|
607
|
+
bad();
|
|
608
|
+
return Object.freeze(out as OwnedStoreRegistryRowV1);
|
|
609
|
+
}
|
|
610
|
+
export function validateOwnedStoreRegistryV1(rows: unknown, defs: readonly OrmOwnedStoreDefinitionV1[]): readonly OwnedStoreRegistryRowV1[] {
|
|
611
|
+
const out = dense(rows, row, 4096);
|
|
612
|
+
const keys = new Set<string>();
|
|
613
|
+
for (const x of out) {
|
|
614
|
+
const descriptor = {
|
|
615
|
+
contract: x.contract, storeKey: x.storeKey, formatVersion: Number(x.formatVersion), ownedScope: { schema: x.ownedSchema, tablePrefix: x.tablePrefix }
|
|
616
|
+
} as OrmOwnedStoreDefinitionV1;
|
|
617
|
+
if (canonicalOwnedStoreScopeHashV1(descriptor) !== x.ownedScopeHash)
|
|
618
|
+
bad();
|
|
619
|
+
if (keys.has(x.storeKey))
|
|
620
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
621
|
+
keys.add(x.storeKey);
|
|
622
|
+
const d = defs.find(y => y.storeKey === x.storeKey);
|
|
623
|
+
if (d && (x.formatVersion !== String(d.formatVersion) || x.ownedSchema !== d.ownedScope.schema || x.tablePrefix !== d.ownedScope.tablePrefix))
|
|
624
|
+
throw failure("ORM_OWNED_STORE_IDENTITY_MISMATCH");
|
|
625
|
+
}
|
|
626
|
+
for (let i = 0; i < out.length; i++)
|
|
627
|
+
for (let j = i + 1; j < out.length; j++) {
|
|
628
|
+
const a = out[i]!, b = out[j]!;
|
|
629
|
+
if (a.ownedSchema === b.ownedSchema && (a.tablePrefix.startsWith(b.tablePrefix) || b.tablePrefix.startsWith(a.tablePrefix)))
|
|
630
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
631
|
+
}
|
|
632
|
+
return out;
|
|
633
|
+
}
|
|
634
|
+
type CatalogBudget = {
|
|
635
|
+
used: number;
|
|
636
|
+
};
|
|
637
|
+
function take(b: CatalogBudget, n = 1): void {
|
|
638
|
+
if (n > 65536 - b.used)
|
|
639
|
+
bad();
|
|
640
|
+
b.used += n;
|
|
641
|
+
}
|
|
642
|
+
function catalogDense<T>(v: unknown, parse: (x: unknown) => T, b: CatalogBudget): readonly T[] {
|
|
643
|
+
if (v === null || typeof v !== "object" || types.isProxy(v) || !Array.isArray(v))
|
|
644
|
+
bad();
|
|
645
|
+
const length = Object.getOwnPropertyDescriptor(v, "length");
|
|
646
|
+
if (!length || !("value" in length) || typeof length.value !== "number" || length.value > 65536)
|
|
647
|
+
bad();
|
|
648
|
+
take(b, length.value);
|
|
649
|
+
return dense(v, parse, 65536);
|
|
650
|
+
}
|
|
651
|
+
function unique<T>(items: readonly T[], key: (x: T) => string): void {
|
|
652
|
+
const keys = new Set<string>();
|
|
653
|
+
for (const x of items) {
|
|
654
|
+
const k = key(x);
|
|
655
|
+
if (keys.has(k))
|
|
656
|
+
bad();
|
|
657
|
+
keys.add(k);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
function nestedIndexes(indexes: readonly OwnedCatalogIndexV1[], relations: readonly OwnedCatalogRelationV1[]): void {
|
|
661
|
+
unique(relations, x => x.oid);
|
|
662
|
+
if (indexes.length !== relations.length)
|
|
663
|
+
bad();
|
|
664
|
+
const expected = [...indexes.map(x => x.indexRelationOid)].sort((a, b) => BigInt(a) < BigInt(b) ? -1 : BigInt(a) > BigInt(b) ? 1 : 0);
|
|
665
|
+
for (let i = 0; i < relations.length; i++)
|
|
666
|
+
if (relations[i]!.oid !== expected[i])
|
|
667
|
+
bad();
|
|
668
|
+
}
|
|
669
|
+
function registryShape(v: unknown, c: OwnedStoreCatalogContextV1, b: CatalogBudget): OwnedStoreRegistryShapeV1 {
|
|
670
|
+
const r = record(v, [
|
|
671
|
+
"catalogClasses", "relation", "rowType", "arrayType", "columns", "indexes", "indexRelations", "constraints", "triggers", "rules", "policies", "inheritance", "dependencies", "sequences", "toast"
|
|
672
|
+
]);
|
|
673
|
+
take(b, 3);
|
|
674
|
+
const out = {
|
|
675
|
+
catalogClasses: catalogDense(r.catalogClasses, parseOwnedCatalogClassV1, b), relation: parseOwnedCatalogRelationV1(r.relation, c), rowType: parseOwnedCatalogRowTypeV1(r.rowType), arrayType: parseOwnedCatalogArrayTypeV1(r.arrayType, c), columns: catalogDense(r.columns, parseOwnedCatalogColumnV1, b), indexes: catalogDense(r.indexes, parseOwnedCatalogIndexV1, b), indexRelations: catalogDense(r.indexRelations, x => parseOwnedCatalogRelationV1(x, c), b), constraints: catalogDense(r.constraints, x => parseOwnedCatalogConstraintV1(x, c), b), triggers: catalogDense(r.triggers, parseOwnedCatalogTriggerV1, b), rules: catalogDense(r.rules, parseOwnedCatalogRuleV1, b), policies: catalogDense(r.policies, parseOwnedCatalogPolicyV1, b), inheritance: catalogDense(r.inheritance, parseOwnedCatalogInheritanceV1, b), dependencies: catalogDense(r.dependencies, parseOwnedCatalogDependencyV1, b), sequences: catalogDense(r.sequences, parseOwnedCatalogSequenceV1, b), toast: r.toast === null ? null : toast(r.toast, c, b)
|
|
676
|
+
};
|
|
677
|
+
const t = out.toast;
|
|
678
|
+
unique([
|
|
679
|
+
...out.catalogClasses, out.relation, ...out.indexRelations, ...(t ? [t.relation, ...t.indexRelations] : [])
|
|
680
|
+
], x => x.oid);
|
|
681
|
+
unique([out.rowType, out.arrayType], x => x.oid);
|
|
682
|
+
unique([...out.columns, ...(t ? t.columns : [])], x => `${x.relationOid}:${x.attnum}`);
|
|
683
|
+
unique([...out.indexes, ...(t ? t.indexes : [])], x => x.indexRelationOid);
|
|
684
|
+
unique(out.constraints, x => x.oid);
|
|
685
|
+
unique(out.constraints, x => `${x.relationOid}:${x.kind}:${x.name}`);
|
|
686
|
+
unique(out.triggers, x => x.oid);
|
|
687
|
+
unique(out.rules, x => x.oid);
|
|
688
|
+
unique(out.policies, x => x.oid);
|
|
689
|
+
unique(out.inheritance, x => `${x.childRelationOid}:${x.parentRelationOid}:${x.sequence}`);
|
|
690
|
+
unique([...out.dependencies, ...(t ? t.dependencies : [])], x => `${x.dependentClassOid}:${x.dependentOid}:${x.dependentSubId}:${x.referencedClassOid}:${x.referencedOid}:${x.referencedSubId}:${x.kind}`);
|
|
691
|
+
unique(out.sequences, x => x.relationOid);
|
|
692
|
+
nestedIndexes(out.indexes, out.indexRelations);
|
|
693
|
+
return Object.freeze(out);
|
|
694
|
+
}
|
|
695
|
+
function toast(v: unknown, c: OwnedStoreCatalogContextV1, b: CatalogBudget): OwnedCatalogToastClosureV1 {
|
|
696
|
+
const r = record(v, ["ownerTableOid", "relation", "columns", "indexes", "indexRelations", "dependencies"]);
|
|
697
|
+
take(b);
|
|
698
|
+
const out = {
|
|
699
|
+
ownerTableOid: oid(r.ownerTableOid), relation: parseOwnedCatalogRelationV1(r.relation, c), columns: catalogDense(r.columns, parseOwnedCatalogColumnV1, b), indexes: catalogDense(r.indexes, parseOwnedCatalogIndexV1, b), indexRelations: catalogDense(r.indexRelations, x => parseOwnedCatalogRelationV1(x, c), b), dependencies: catalogDense(r.dependencies, parseOwnedCatalogDependencyV1, b)
|
|
700
|
+
};
|
|
701
|
+
unique(out.columns, x => `${x.relationOid}:${x.attnum}`);
|
|
702
|
+
unique(out.indexes, x => x.indexRelationOid);
|
|
703
|
+
unique(out.dependencies, x => `${x.dependentClassOid}:${x.dependentOid}:${x.dependentSubId}:${x.referencedClassOid}:${x.referencedOid}:${x.referencedSubId}:${x.kind}`);
|
|
704
|
+
nestedIndexes(out.indexes, out.indexRelations);
|
|
705
|
+
return Object.freeze(out);
|
|
706
|
+
}
|
|
707
|
+
export function parseOwnedStoreRegistrySnapshotV1(v: unknown, defs: readonly OrmOwnedStoreDefinitionV1[], context: OwnedStoreCatalogContextV1): OwnedStoreRegistrySnapshotV1 {
|
|
708
|
+
const c = parseOwnedStoreCatalogContextV1(context);
|
|
709
|
+
const r = record(v, ["contract", "publicSchemaExists", "state"]);
|
|
710
|
+
if (r.contract !== "bazis.orm-owned-store-registry-snapshot/v1" || typeof r.publicSchemaExists !== "boolean")
|
|
711
|
+
bad();
|
|
712
|
+
const s = record(r.state, ["kind", "shape", "rows"], ["kind"]);
|
|
713
|
+
if (s.kind === "absent") {
|
|
714
|
+
if (Object.keys(s).length !== 1)
|
|
715
|
+
bad();
|
|
716
|
+
if (!r.publicSchemaExists)
|
|
717
|
+
throw failure("ORM_OWNED_STORE_CREATE_FAILED");
|
|
718
|
+
return Object.freeze({ contract: r.contract, publicSchemaExists: r.publicSchemaExists, state: Object.freeze({ kind: "absent" }) });
|
|
719
|
+
}
|
|
720
|
+
if (s.kind !== "present")
|
|
721
|
+
bad();
|
|
722
|
+
const b: CatalogBudget = { used: 0 };
|
|
723
|
+
const shape = registryShape(s.shape, c, b), rows = validateOwnedStoreRegistryV1(s.rows, defs);
|
|
724
|
+
return Object.freeze({
|
|
725
|
+
contract: r.contract, publicSchemaExists: r.publicSchemaExists, state: Object.freeze({ kind: "present", shape, rows })
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
export function parseOwnedStoreCatalogSnapshotV1(v: unknown, context: OwnedStoreCatalogContextV1): OwnedStoreCatalogSnapshotV1 {
|
|
729
|
+
const c = parseOwnedStoreCatalogContextV1(context), names = [
|
|
730
|
+
"contract", "requestedScopes", "existingSchemas", "catalogClasses", "relations", "rowTypes", "arrayTypes", "columns", "indexes", "constraints", "triggers", "rules", "policies", "inheritance", "dependencies", "sequences"
|
|
731
|
+
], r = record(v, names);
|
|
732
|
+
if (r.contract !== "bazis.orm-owned-store-catalog-snapshot/v1")
|
|
733
|
+
bad();
|
|
734
|
+
const compare = (a: string, b: string) => Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8")), scopes = dense(r.requestedScopes, x => {
|
|
735
|
+
const s = record(x, ["schema", "tablePrefix"]);
|
|
736
|
+
return Object.freeze({ schema: text(s.schema, 1, 63), tablePrefix: text(s.tablePrefix, 1, 63) });
|
|
737
|
+
}, 8320);
|
|
738
|
+
for (let i = 1; i < scopes.length; i++) {
|
|
739
|
+
const prev = scopes[i - 1]!, next = scopes[i]!, schema = compare(prev.schema, next.schema);
|
|
740
|
+
if (schema > 0 || schema === 0 && compare(prev.tablePrefix, next.tablePrefix) >= 0)
|
|
741
|
+
bad();
|
|
742
|
+
}
|
|
743
|
+
const schemas = dense(r.existingSchemas, x => text(x, 1, 63), 8320), scopeSchemas = new Set(scopes.map(x => x.schema));
|
|
744
|
+
for (let i = 0; i < schemas.length; i++) {
|
|
745
|
+
if (!scopeSchemas.has(schemas[i]!))
|
|
746
|
+
bad();
|
|
747
|
+
if (i && compare(schemas[i - 1]!, schemas[i]!) >= 0)
|
|
748
|
+
bad();
|
|
749
|
+
}
|
|
750
|
+
const b: CatalogBudget = { used: 0 }, out = {
|
|
751
|
+
contract: "bazis.orm-owned-store-catalog-snapshot/v1" as const, requestedScopes: scopes, existingSchemas: schemas, catalogClasses: catalogDense(r.catalogClasses, parseOwnedCatalogClassV1, b), relations: catalogDense(r.relations, x => parseOwnedCatalogRelationV1(x, c), b), rowTypes: catalogDense(r.rowTypes, parseOwnedCatalogRowTypeV1, b), arrayTypes: catalogDense(r.arrayTypes, x => parseOwnedCatalogArrayTypeV1(x, c), b), columns: catalogDense(r.columns, parseOwnedCatalogColumnV1, b), indexes: catalogDense(r.indexes, parseOwnedCatalogIndexV1, b), constraints: catalogDense(r.constraints, x => parseOwnedCatalogConstraintV1(x, c), b), triggers: catalogDense(r.triggers, parseOwnedCatalogTriggerV1, b), rules: catalogDense(r.rules, parseOwnedCatalogRuleV1, b), policies: catalogDense(r.policies, parseOwnedCatalogPolicyV1, b), inheritance: catalogDense(r.inheritance, parseOwnedCatalogInheritanceV1, b), dependencies: catalogDense(r.dependencies, parseOwnedCatalogDependencyV1, b), sequences: catalogDense(r.sequences, parseOwnedCatalogSequenceV1, b)
|
|
752
|
+
};
|
|
753
|
+
unique([...out.catalogClasses, ...out.relations], x => x.oid);
|
|
754
|
+
unique([...out.rowTypes, ...out.arrayTypes], x => x.oid);
|
|
755
|
+
unique(out.columns, x => `${x.relationOid}:${x.attnum}`);
|
|
756
|
+
unique(out.indexes, x => x.indexRelationOid);
|
|
757
|
+
unique(out.constraints, x => x.oid);
|
|
758
|
+
unique(out.constraints, x => `${x.relationOid}:${x.kind}:${x.name}`);
|
|
759
|
+
unique(out.triggers, x => x.oid);
|
|
760
|
+
unique(out.rules, x => x.oid);
|
|
761
|
+
unique(out.policies, x => x.oid);
|
|
762
|
+
unique(out.inheritance, x => `${x.childRelationOid}:${x.parentRelationOid}:${x.sequence}`);
|
|
763
|
+
unique(out.dependencies, x => `${x.dependentClassOid}:${x.dependentOid}:${x.dependentSubId}:${x.referencedClassOid}:${x.referencedOid}:${x.referencedSubId}:${x.kind}`);
|
|
764
|
+
unique(out.sequences, x => x.relationOid);
|
|
765
|
+
return Object.freeze(out);
|
|
766
|
+
}
|
|
767
|
+
export interface OwnedStoreCatalogStoreExpectationV1 {
|
|
768
|
+
readonly definition: Readonly<OrmOwnedStoreDefinitionV1>;
|
|
769
|
+
readonly expectedSchema: OrmExpectedSchema;
|
|
770
|
+
}
|
|
771
|
+
export interface OwnedStoreCatalogSemanticContextV1 {
|
|
772
|
+
readonly stores: readonly OwnedStoreCatalogStoreExpectationV1[];
|
|
773
|
+
readonly requestedScopes: readonly OrmCatalogScopeV1[];
|
|
774
|
+
}
|
|
775
|
+
interface OwnedStoreCatalogOwnershipAnalysisV1 {
|
|
776
|
+
readonly stores: readonly OwnedStoreCatalogStoreExpectationV1[];
|
|
777
|
+
readonly rootsByStoreKey: ReadonlyMap<string, readonly OwnedCatalogRelationV1[]>;
|
|
778
|
+
}
|
|
779
|
+
function analyzeOwnedStoreCatalogContextV1(snapshot: OwnedStoreCatalogSnapshotV1, context: OwnedStoreCatalogSemanticContextV1): OwnedStoreCatalogOwnershipAnalysisV1 {
|
|
780
|
+
if (!Array.isArray(context.stores) || context.stores.length < 1 || context.stores.length > 128)
|
|
781
|
+
bad();
|
|
782
|
+
const stores = context.stores.map((store) => {
|
|
783
|
+
if (!store || !isDefinedOrmOwnedStoreV1(store.definition))
|
|
784
|
+
throw failure("ORM_OWNED_STORE_IDENTITY_MISMATCH");
|
|
785
|
+
if (!store.expectedSchema)
|
|
786
|
+
bad();
|
|
787
|
+
return store;
|
|
788
|
+
});
|
|
789
|
+
const keys = new Set<string>();
|
|
790
|
+
const compare = (left: OrmCatalogScopeV1, right: OrmCatalogScopeV1): number => Buffer.compare(Buffer.from(left.schema, "utf8"), Buffer.from(right.schema, "utf8")) || Buffer.compare(Buffer.from(left.tablePrefix, "utf8"), Buffer.from(right.tablePrefix, "utf8"));
|
|
791
|
+
const overlap = (left: OrmCatalogScopeV1, right: OrmCatalogScopeV1): boolean => left.schema === right.schema && (left.tablePrefix.startsWith(right.tablePrefix) || right.tablePrefix.startsWith(left.tablePrefix));
|
|
792
|
+
const owned = stores.map((store) => store.definition.ownedScope);
|
|
793
|
+
for (let index = 0; index < stores.length; index++) {
|
|
794
|
+
if (keys.has(stores[index]!.definition.storeKey))
|
|
795
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
796
|
+
keys.add(stores[index]!.definition.storeKey);
|
|
797
|
+
for (let other = index + 1; other < stores.length; other++)
|
|
798
|
+
if (overlap(owned[index]!, owned[other]!))
|
|
799
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
800
|
+
}
|
|
801
|
+
const all = stores.flatMap((store) => [store.definition.ownedScope, ...(store.definition.rejectIfPresent ?? [])]).sort(compare);
|
|
802
|
+
const union = all.filter((scope, index) => index === 0 || scope.schema !== all[index - 1]!.schema || scope.tablePrefix !== all[index - 1]!.tablePrefix);
|
|
803
|
+
for (const store of stores)
|
|
804
|
+
for (const reject of store.definition.rejectIfPresent ?? [])
|
|
805
|
+
for (const scope of owned)
|
|
806
|
+
if (overlap(scope, reject))
|
|
807
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
808
|
+
if (union.some((scope) => scope.schema === "public" && "__bazis_orm_owned_stores_v1".startsWith(scope.tablePrefix)))
|
|
809
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
810
|
+
if (!Array.isArray(context.requestedScopes) || union.length !== context.requestedScopes.length || union.length !== snapshot.requestedScopes.length)
|
|
811
|
+
bad();
|
|
812
|
+
for (let index = 0; index < union.length; index++) {
|
|
813
|
+
const expected = union[index]!, requested = context.requestedScopes[index]!, captured = snapshot.requestedScopes[index]!;
|
|
814
|
+
if (expected.schema !== requested.schema || expected.tablePrefix !== requested.tablePrefix || expected.schema !== captured.schema || expected.tablePrefix !== captured.tablePrefix)
|
|
815
|
+
bad();
|
|
816
|
+
}
|
|
817
|
+
const rootsByStoreKey = new Map<string, readonly OwnedCatalogRelationV1[]>();
|
|
818
|
+
for (const store of stores)
|
|
819
|
+
rootsByStoreKey.set(store.definition.storeKey, Object.freeze(snapshot.relations.filter((relation) => relation.kind === "ordinaryTable" && relation.schema === store.definition.ownedScope.schema && relation.name.startsWith(store.definition.ownedScope.tablePrefix))));
|
|
820
|
+
for (const store of stores)
|
|
821
|
+
for (const reject of store.definition.rejectIfPresent ?? [])
|
|
822
|
+
if (snapshot.relations.some((relation) => relation.schema === reject.schema && relation.name.startsWith(reject.tablePrefix)))
|
|
823
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
824
|
+
const ownerOf = (oid: string): string | undefined => stores.find((store) => rootsByStoreKey.get(store.definition.storeKey)!.some((relation) => relation.oid === oid))?.definition.storeKey;
|
|
825
|
+
for (const constraint of snapshot.constraints)
|
|
826
|
+
if (constraint.kind === "foreignKey" && constraint.referencedRelationOid !== null) {
|
|
827
|
+
const target = ownerOf(constraint.referencedRelationOid);
|
|
828
|
+
if (target && ownerOf(constraint.relationOid) !== target)
|
|
829
|
+
throw failure("ORM_OWNED_STORE_OWNERSHIP_CONFLICT");
|
|
830
|
+
}
|
|
831
|
+
return Object.freeze({ stores: Object.freeze([...stores]), rootsByStoreKey });
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Checks that an owned table's TOAST table, if any, has exactly the shape
|
|
835
|
+
* PostgreSQL creates: `pg_toast` schema, `chunk_id`/`chunk_seq`/`chunk_data`
|
|
836
|
+
* columns, one index and only the expected internal dependencies. Works on one
|
|
837
|
+
* immutable, already parsed catalog snapshot.
|
|
838
|
+
*/
|
|
839
|
+
function validateToastClosureV1(owner: OwnedCatalogRelationV1, relations: ReadonlyMap<string, OwnedCatalogRelationV1>, columns: ReadonlyMap<string, OwnedCatalogColumnV1[]>, indexes: ReadonlyMap<string, OwnedCatalogIndexV1[]>, consumedToastRelationOids: Set<string>, pgClass: string, pgNamespace: string, consume: (dependentClassOid: string, dependentOid: string, dependentSubId: string, referencedClassOid: string, referencedOid: string, referencedSubId: string, kind: OwnedCatalogDependencyV1["kind"], count?: number) => void, rejectDirect: (dependentClassOid: string, dependentOid: string, referencedClassOid: string, referencedOid: string) => void): void {
|
|
840
|
+
if (owner.toastRelationOid === null)
|
|
841
|
+
return;
|
|
842
|
+
const toast = relations.get(owner.toastRelationOid);
|
|
843
|
+
if (!toast || toast.kind !== "toastTable" || toast.rawKind !== "t" || toast.schema !== "pg_toast" || toast.persistence !== "permanent" || toast.accessMethod !== owner.accessMethod || toast.tablespaceOid !== owner.tablespaceOid || toast.isPartition || toast.rowSecurity || toast.forceRowSecurity || toast.replicaIdentity !== "nothing" || toast.options.length || toast.rowTypeOid !== null || toast.toastRelationOid !== null || consumedToastRelationOids.has(toast.oid))
|
|
844
|
+
bad();
|
|
845
|
+
consumedToastRelationOids.add(toast.oid);
|
|
846
|
+
consume(pgClass, toast.oid, "0", pgClass, owner.oid, "0", "internal");
|
|
847
|
+
rejectDirect(pgClass, toast.oid, pgNamespace, toast.namespaceOid);
|
|
848
|
+
const toastColumns = [...(columns.get(toast.oid) ?? [])].sort((left, right) => Number(left.attnum) - Number(right.attnum));
|
|
849
|
+
const expectedColumns = [["chunk_id", "oid"], ["chunk_seq", "integer"], ["chunk_data", "bytea"]] as const;
|
|
850
|
+
if (toastColumns.length !== expectedColumns.length)
|
|
851
|
+
bad();
|
|
852
|
+
for (let index = 0; index < toastColumns.length; index++) {
|
|
853
|
+
const column = toastColumns[index]!, expected = expectedColumns[index]!;
|
|
854
|
+
if (column.attnum !== String(index + 1) || column.name !== expected[0] || column.physicalType !== expected[1] || column.typeOid === "0" || BigInt(column.typeOid) >= 12000n || column.dropped || !column.local || column.inheritanceCount !== "0" || column.notNull || column.default.kind !== "none" || column.defaultObjectOid !== null || column.generation !== "none" || column.identityCode !== "" || column.generatedCode !== "" || column.collationOid !== column.typeDefaultCollationOid || column.storageCode !== "p" || column.compressionCode !== "")
|
|
855
|
+
bad();
|
|
856
|
+
}
|
|
857
|
+
const toastIndexes = indexes.get(toast.oid) ?? [];
|
|
858
|
+
if (toastIndexes.length !== 1)
|
|
859
|
+
bad();
|
|
860
|
+
const index = toastIndexes[0]!, indexRelation = relations.get(index.indexRelationOid);
|
|
861
|
+
if (!indexRelation || indexRelation.kind !== "index" || indexRelation.rawKind !== "i" || indexRelation.schema !== "pg_toast" || indexRelation.namespaceOid !== toast.namespaceOid || indexRelation.name !== index.name || indexRelation.persistence !== "permanent" || indexRelation.isPartition || indexRelation.rowSecurity || indexRelation.forceRowSecurity || indexRelation.replicaIdentity !== "nothing" || indexRelation.tablespaceOid !== "0" || indexRelation.accessMethod !== "btree" || indexRelation.options.length || indexRelation.rowTypeOid !== null || indexRelation.toastRelationOid !== null || index.tableRelationOid !== toast.oid || index.method !== "btree" || !index.primary || !index.unique || index.exclusion || !index.immediate || !index.valid || !index.ready || !index.live || index.replicaIdentity || index.nullsNotDistinct || index.keyAttributeCount !== "2" || index.totalAttributeCount !== "2" || index.attributeNumbers.length !== 2 || index.attributeNumbers[0] !== "1" || index.attributeNumbers[1] !== "2" || index.columnNames.length !== 2 || index.columnNames[0] !== "chunk_id" || index.columnNames[1] !== "chunk_seq" || index.collationOids.length !== 2 || index.collationOids.some((oid, position) => oid !== toastColumns[position]?.typeDefaultCollationOid) || index.opclassOids.length !== 2 || index.defaultOpclassOids.length !== 2 || index.opclassOids.some((oid, position) => oid !== index.defaultOpclassOids[position] || BigInt(oid) >= 12000n) || index.options.length !== 2 || index.options.some((option) => option !== "0") || index.expression !== null || index.predicate !== null || index.backingConstraintOid !== null)
|
|
862
|
+
bad();
|
|
863
|
+
consume(pgClass, index.indexRelationOid, "0", pgClass, toast.oid, "1", "automatic");
|
|
864
|
+
consume(pgClass, index.indexRelationOid, "0", pgClass, toast.oid, "2", "automatic");
|
|
865
|
+
rejectDirect(pgClass, index.indexRelationOid, pgNamespace, indexRelation.namespaceOid);
|
|
866
|
+
}
|
|
867
|
+
export function validateFixedRegistryShapeV1(shape: OwnedStoreRegistryShapeV1): void {
|
|
868
|
+
if (shape.relation.schema !== "public" || shape.relation.name !== "__bazis_orm_owned_stores_v1" || shape.relation.kind !== "ordinaryTable" || shape.relation.rawKind !== "r" || shape.relation.persistence !== "permanent" || shape.relation.isPartition || shape.relation.rowSecurity || shape.relation.forceRowSecurity || shape.relation.replicaIdentity !== "default" || shape.relation.tablespaceOid !== "0" || shape.relation.accessMethod !== "heap" || shape.relation.options.length || shape.relation.rowTypeOid !== shape.rowType.oid || shape.relation.toastRelationOid !== (shape.toast?.relation.oid ?? null))
|
|
869
|
+
bad();
|
|
870
|
+
if (shape.toast !== null && shape.toast.ownerTableOid !== shape.relation.oid)
|
|
871
|
+
bad();
|
|
872
|
+
unique([
|
|
873
|
+
...shape.catalogClasses, shape.relation, ...shape.indexRelations, ...(shape.toast === null ? [] : [shape.toast.relation, ...shape.toast.indexRelations])
|
|
874
|
+
], (entry) => JSON.stringify([entry.schema, entry.name]));
|
|
875
|
+
unique([shape.rowType, shape.arrayType], (entry) => JSON.stringify([entry.schema, entry.name]));
|
|
876
|
+
if (shape.columns.length !== 8 || shape.indexes.length !== 1 || shape.indexRelations.length !== 1 || shape.constraints.length !== 1 || shape.triggers.length || shape.rules.length || shape.policies.length || shape.inheritance.length || shape.sequences.length)
|
|
877
|
+
bad();
|
|
878
|
+
const columns = [
|
|
879
|
+
["store_key", "text"], ["contract", "text"], ["format_version", "integer"], ["owned_schema", "text"], ["table_prefix", "text"], ["owned_scope_hash", "text"], ["model_hash", "text"], ["created_at", "datetime"]
|
|
880
|
+
] as const;
|
|
881
|
+
const orderedColumns = [...shape.columns].sort((left, right) => Number(left.attnum) - Number(right.attnum));
|
|
882
|
+
for (let index = 0; index < columns.length; index++) {
|
|
883
|
+
const column = orderedColumns[index]!, [name, physicalType] = columns[index]!;
|
|
884
|
+
if (column.relationOid !== shape.relation.oid || column.attnum !== String(index + 1) || column.name !== name || column.physicalType !== physicalType || column.typeOid === "0" || BigInt(column.typeOid) >= 12000n || !column.notNull || column.dropped || !column.local || column.inheritanceCount !== "0" || column.default.kind !== "none" || column.defaultObjectOid !== null || column.generation !== "none" || column.identityCode !== "" || column.generatedCode !== "" || column.collationOid !== column.typeDefaultCollationOid || column.storageCode !== column.typeDefaultStorageCode || column.compressionCode !== "")
|
|
885
|
+
bad();
|
|
886
|
+
}
|
|
887
|
+
if (shape.rowType.relationOid !== shape.relation.oid || shape.rowType.schema !== "public" || shape.rowType.name !== shape.relation.name || shape.rowType.kind !== "composite" || shape.rowType.arrayTypeOid !== shape.arrayType.oid || shape.arrayType.elementTypeOid !== shape.rowType.oid || shape.arrayType.relationOid !== "0" || shape.arrayType.arrayTypeOid !== "0" || shape.arrayType.schema !== "public" || shape.arrayType.kind !== "base" || shape.arrayType.category !== "array")
|
|
888
|
+
bad();
|
|
889
|
+
const index = shape.indexes[0]!, indexRelation = shape.indexRelations[0]!, primary = shape.constraints[0]!;
|
|
890
|
+
if (primary.relationOid !== shape.relation.oid || primary.name !== "__bazis_orm_owned_stores_v1_pkey" || primary.kind !== "primaryKey" || primary.columns.length !== 1 || primary.columns[0] !== "store_key" || primary.referencedRelationOid !== null || primary.referencedColumns.length || primary.backingIndexOid !== index.indexRelationOid || !primary.noInherit || primary.parentConstraintOid !== null || primary.inheritanceCount !== "0" || !primary.validated || primary.deferrable || primary.initiallyDeferred || primary.onDelete !== null || primary.onUpdate !== null || primary.match !== null || primary.deleteSetColumns.length || primary.primaryForeignEqualityOperatorOids.length || primary.primaryPrimaryEqualityOperatorOids.length || primary.foreignForeignEqualityOperatorOids.length || primary.defaultEqualityOperatorOids.length || primary.checkExpression !== null || index.indexRelationOid !== indexRelation.oid || index.tableRelationOid !== shape.relation.oid || index.name !== "__bazis_orm_owned_stores_v1_pkey" || !index.unique || !index.primary || index.exclusion || !index.immediate || !index.valid || !index.ready || !index.live || index.replicaIdentity || index.nullsNotDistinct || index.method !== "btree" || index.keyAttributeCount !== "1" || index.totalAttributeCount !== "1" || index.attributeNumbers.length !== 1 || index.attributeNumbers[0] !== "1" || index.columnNames.length !== 1 || index.columnNames[0] !== "store_key" || index.collationOids.length !== 1 || index.collationOids[0] !== orderedColumns[0]!.typeDefaultCollationOid || index.opclassOids.length !== 1 || index.defaultOpclassOids.length !== 1 || index.opclassOids[0] !== index.defaultOpclassOids[0] || BigInt(index.opclassOids[0]!) >= 12000n || index.options.length !== 1 || index.options[0] !== "0" || index.expression !== null || index.predicate !== null || index.backingConstraintOid !== primary.oid || indexRelation.namespaceOid !== shape.relation.namespaceOid || indexRelation.schema !== "public" || indexRelation.name !== index.name || indexRelation.kind !== "index" || indexRelation.rawKind !== "i" || indexRelation.persistence !== "permanent" || indexRelation.isPartition || indexRelation.rowSecurity || indexRelation.forceRowSecurity || indexRelation.replicaIdentity !== "nothing" || indexRelation.tablespaceOid !== "0" || indexRelation.accessMethod !== "btree" || indexRelation.options.length || indexRelation.rowTypeOid !== null || indexRelation.toastRelationOid !== null)
|
|
891
|
+
bad();
|
|
892
|
+
unique(shape.catalogClasses, (entry) => entry.name);
|
|
893
|
+
for (const entry of shape.catalogClasses)
|
|
894
|
+
if (entry.schema !== "pg_catalog" || entry.kind === "other" || entry.name !== entry.kind)
|
|
895
|
+
bad();
|
|
896
|
+
const classes = new Map(shape.catalogClasses.map((entry) => [entry.name, entry]));
|
|
897
|
+
const classOid = (name: string, kind: OwnedCatalogClassV1["kind"]): string => {
|
|
898
|
+
const entry = classes.get(name);
|
|
899
|
+
if (!entry || entry.schema !== "pg_catalog" || entry.kind !== kind || entry.name !== kind)
|
|
900
|
+
bad();
|
|
901
|
+
return entry.oid;
|
|
902
|
+
};
|
|
903
|
+
const pgClass = classOid("pg_class", "pg_class"), pgType = classOid("pg_type", "pg_type"), pgConstraint = classOid("pg_constraint", "pg_constraint"), pgNamespace = classOid("pg_namespace", "pg_namespace");
|
|
904
|
+
const namespaceBySchema = new Map<string, string>(), schemaByNamespace = new Map<string, string>();
|
|
905
|
+
for (const relation of [
|
|
906
|
+
shape.relation, ...shape.indexRelations, ...(shape.toast === null ? [] : [shape.toast.relation, ...shape.toast.indexRelations])
|
|
907
|
+
]) {
|
|
908
|
+
const knownNamespace = namespaceBySchema.get(relation.schema), knownSchema = schemaByNamespace.get(relation.namespaceOid);
|
|
909
|
+
if (knownNamespace !== undefined && knownNamespace !== relation.namespaceOid || knownSchema !== undefined && knownSchema !== relation.schema)
|
|
910
|
+
bad();
|
|
911
|
+
namespaceBySchema.set(relation.schema, relation.namespaceOid);
|
|
912
|
+
schemaByNamespace.set(relation.namespaceOid, relation.schema);
|
|
913
|
+
}
|
|
914
|
+
const dependencies = [...shape.dependencies, ...(shape.toast?.dependencies ?? [])];
|
|
915
|
+
const remaining = new Set(dependencies.map((_edge, position) => position));
|
|
916
|
+
const consume = (dependentClassOid: string, dependentOid: string, dependentSubId: string, referencedClassOid: string, referencedOid: string, referencedSubId: string, kind: OwnedCatalogDependencyV1["kind"]): void => {
|
|
917
|
+
const matches = [...remaining].filter((position) => {
|
|
918
|
+
const edge = dependencies[position]!;
|
|
919
|
+
return edge.dependentClassOid === dependentClassOid && edge.dependentOid === dependentOid && edge.dependentSubId === dependentSubId && edge.referencedClassOid === referencedClassOid && edge.referencedOid === referencedOid && edge.referencedSubId === referencedSubId && edge.kind === kind;
|
|
920
|
+
});
|
|
921
|
+
if (matches.length !== 1)
|
|
922
|
+
bad();
|
|
923
|
+
remaining.delete(matches[0]!);
|
|
924
|
+
};
|
|
925
|
+
const rejectDirect = (dependentClassOid: string, dependentOid: string, referencedClassOid: string, referencedOid: string): void => {
|
|
926
|
+
if (dependencies.some((edge) => edge.dependentClassOid === dependentClassOid && edge.dependentOid === dependentOid && edge.referencedClassOid === referencedClassOid && edge.referencedOid === referencedOid))
|
|
927
|
+
bad();
|
|
928
|
+
};
|
|
929
|
+
if (shape.relation.namespaceOid === "2200" || BigInt(shape.relation.namespaceOid) >= 12000n)
|
|
930
|
+
consume(pgClass, shape.relation.oid, "0", pgNamespace, shape.relation.namespaceOid, "0", "normal");
|
|
931
|
+
else
|
|
932
|
+
rejectDirect(pgClass, shape.relation.oid, pgNamespace, shape.relation.namespaceOid);
|
|
933
|
+
consume(pgType, shape.rowType.oid, "0", pgClass, shape.relation.oid, "0", "internal");
|
|
934
|
+
consume(pgType, shape.arrayType.oid, "0", pgType, shape.rowType.oid, "0", "internal");
|
|
935
|
+
consume(pgConstraint, primary.oid, "0", pgClass, shape.relation.oid, "1", "automatic");
|
|
936
|
+
consume(pgClass, indexRelation.oid, "0", pgConstraint, primary.oid, "0", "internal");
|
|
937
|
+
rejectDirect(pgClass, indexRelation.oid, pgNamespace, indexRelation.namespaceOid);
|
|
938
|
+
if (shape.toast !== null) {
|
|
939
|
+
if (shape.toast.columns.length !== 3 || shape.toast.indexes.length !== 1 || shape.toast.indexRelations.length !== 1 || shape.toast.columns.some((column) => column.relationOid !== shape.toast!.relation.oid))
|
|
940
|
+
bad();
|
|
941
|
+
const toastRelations = new Map<string, OwnedCatalogRelationV1>([
|
|
942
|
+
[shape.relation.oid, shape.relation],
|
|
943
|
+
[shape.toast.relation.oid, shape.toast.relation],
|
|
944
|
+
...shape.toast.indexRelations.map((relation) => [relation.oid, relation] as const),
|
|
945
|
+
]);
|
|
946
|
+
const toastColumns = new Map<string, OwnedCatalogColumnV1[]>([[shape.toast.relation.oid, [...shape.toast.columns]]]);
|
|
947
|
+
const toastIndexes = new Map<string, OwnedCatalogIndexV1[]>([[shape.toast.relation.oid, [...shape.toast.indexes]]]);
|
|
948
|
+
validateToastClosureV1(shape.relation, toastRelations, toastColumns, toastIndexes, new Set<string>(), pgClass, pgNamespace, consume, rejectDirect);
|
|
949
|
+
}
|
|
950
|
+
if (remaining.size)
|
|
951
|
+
bad();
|
|
952
|
+
}
|
|
953
|
+
export function verifyOwnedStoreCatalogAllV1(snapshot: OwnedStoreCatalogSnapshotV1, context: OwnedStoreCatalogSemanticContextV1): void {
|
|
954
|
+
const analysis = analyzeOwnedStoreCatalogContextV1(snapshot, context);
|
|
955
|
+
verifyOwnedStoreCatalogModelsV1(snapshot, analysis, analysis.stores);
|
|
956
|
+
}
|
|
957
|
+
export function inspectOwnedStoreCatalogPreCreateV1(snapshot: OwnedStoreCatalogSnapshotV1, registry: OwnedStoreRegistrySnapshotV1, context: OwnedStoreCatalogSemanticContextV1): {
|
|
958
|
+
readonly kind: "ready";
|
|
959
|
+
readonly emptyMissingIdentityStoreKeys: readonly string[];
|
|
960
|
+
} | {
|
|
961
|
+
readonly kind: "occupiedMissingIdentity";
|
|
962
|
+
readonly storeKeys: readonly string[];
|
|
963
|
+
} {
|
|
964
|
+
const analysis = analyzeOwnedStoreCatalogContextV1(snapshot, context);
|
|
965
|
+
if (registry.state.kind === "present" && !registry.publicSchemaExists)
|
|
966
|
+
bad();
|
|
967
|
+
if (registry.state.kind === "present")
|
|
968
|
+
validateFixedRegistryShapeV1(registry.state.shape);
|
|
969
|
+
const identities = new Set(registry.state.kind === "present" ? registry.state.rows.map((row) => row.storeKey) : []);
|
|
970
|
+
const occupied = analysis.stores.filter((store) => snapshot.relations.some((relation) => relation.schema === store.definition.ownedScope.schema && relation.name.startsWith(store.definition.ownedScope.tablePrefix)));
|
|
971
|
+
const missingOccupied = occupied.filter((store) => !identities.has(store.definition.storeKey));
|
|
972
|
+
if (missingOccupied.length)
|
|
973
|
+
return Object.freeze({
|
|
974
|
+
kind: "occupiedMissingIdentity" as const, storeKeys: Object.freeze(missingOccupied.map((store) => store.definition.storeKey))
|
|
975
|
+
});
|
|
976
|
+
const missing = analysis.stores.filter((store) => !identities.has(store.definition.storeKey));
|
|
977
|
+
for (const store of analysis.stores)
|
|
978
|
+
if (identities.has(store.definition.storeKey) && (analysis.rootsByStoreKey.get(store.definition.storeKey)?.length ?? 0) === 0)
|
|
979
|
+
bad();
|
|
980
|
+
verifyOwnedStoreCatalogModelsV1(snapshot, analysis, analysis.stores.filter((store) => identities.has(store.definition.storeKey)));
|
|
981
|
+
return Object.freeze({
|
|
982
|
+
kind: "ready" as const, emptyMissingIdentityStoreKeys: Object.freeze(missing.map((store) => store.definition.storeKey))
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
function verifyOwnedStoreCatalogModelsV1(snapshot: OwnedStoreCatalogSnapshotV1, analysis: OwnedStoreCatalogOwnershipAnalysisV1, modelStores: readonly OwnedStoreCatalogStoreExpectationV1[]): void {
|
|
986
|
+
unique([...snapshot.catalogClasses, ...snapshot.relations], (entry) => JSON.stringify([entry.schema, entry.name]));
|
|
987
|
+
unique([...snapshot.rowTypes, ...snapshot.arrayTypes], (entry) => JSON.stringify([entry.schema, entry.name]));
|
|
988
|
+
const tableKey = (schema: string, table: string): string => JSON.stringify([schema, table]);
|
|
989
|
+
const expectedTables = new Map(modelStores.flatMap((store) => store.expectedSchema.tables).map((table) => [tableKey(table.schema, table.table), table]));
|
|
990
|
+
const relations = new Map(snapshot.relations.map((relation) => [relation.oid, relation]));
|
|
991
|
+
const owned = new Set([...analysis.rootsByStoreKey.values()].flatMap((roots) => roots.map((relation) => relation.oid)));
|
|
992
|
+
const namespaceBySchema = new Map<string, string>(), schemaByNamespace = new Map<string, string>();
|
|
993
|
+
for (const relation of snapshot.relations) {
|
|
994
|
+
const knownNamespace = namespaceBySchema.get(relation.schema), knownSchema = schemaByNamespace.get(relation.namespaceOid);
|
|
995
|
+
if (knownNamespace !== undefined && knownNamespace !== relation.namespaceOid || knownSchema !== undefined && knownSchema !== relation.schema)
|
|
996
|
+
bad();
|
|
997
|
+
namespaceBySchema.set(relation.schema, relation.namespaceOid);
|
|
998
|
+
schemaByNamespace.set(relation.namespaceOid, relation.schema);
|
|
999
|
+
}
|
|
1000
|
+
const attrdefOwners = new Set<string>();
|
|
1001
|
+
for (const column of snapshot.columns)
|
|
1002
|
+
if (column.defaultObjectOid !== null) {
|
|
1003
|
+
if (attrdefOwners.has(column.defaultObjectOid))
|
|
1004
|
+
bad();
|
|
1005
|
+
attrdefOwners.add(column.defaultObjectOid);
|
|
1006
|
+
}
|
|
1007
|
+
unique(snapshot.rowTypes, (rowType) => rowType.relationOid);
|
|
1008
|
+
unique(snapshot.arrayTypes, (arrayType) => arrayType.elementTypeOid);
|
|
1009
|
+
unique(snapshot.catalogClasses, (entry) => entry.name);
|
|
1010
|
+
for (const entry of snapshot.catalogClasses)
|
|
1011
|
+
if (entry.schema !== "pg_catalog" || entry.kind === "other" || entry.name !== entry.kind)
|
|
1012
|
+
bad();
|
|
1013
|
+
const classes = new Map(snapshot.catalogClasses.map((entry) => [entry.name, entry]));
|
|
1014
|
+
const classOid = (name: string, kind: OwnedCatalogClassV1["kind"]): string => {
|
|
1015
|
+
const entry = classes.get(name);
|
|
1016
|
+
if (!entry || entry.schema !== "pg_catalog" || entry.kind !== kind)
|
|
1017
|
+
bad();
|
|
1018
|
+
return entry.oid;
|
|
1019
|
+
};
|
|
1020
|
+
const pgClass = classOid("pg_class", "pg_class"), pgType = classOid("pg_type", "pg_type"), pgConstraint = classOid("pg_constraint", "pg_constraint"), pgAttrdef = classOid("pg_attrdef", "pg_attrdef"), pgNamespace = classOid("pg_namespace", "pg_namespace");
|
|
1021
|
+
const remaining = new Set(snapshot.dependencies.map((_edge, index) => index));
|
|
1022
|
+
const dependencyKey = (dependentClassOid: string, dependentOid: string, dependentSubId: string, referencedClassOid: string, referencedOid: string, referencedSubId: string, kind: OwnedCatalogDependencyV1["kind"]): string => JSON.stringify([
|
|
1023
|
+
dependentClassOid, dependentOid, dependentSubId, referencedClassOid, referencedOid, referencedSubId, kind
|
|
1024
|
+
]);
|
|
1025
|
+
const directKey = (dependentClassOid: string, dependentOid: string, referencedClassOid: string, referencedOid: string): string => JSON.stringify([dependentClassOid, dependentOid, referencedClassOid, referencedOid]);
|
|
1026
|
+
const dependencyIndexes = new Map<string, number[]>(), directDependencyIndexes = new Map<string, number[]>();
|
|
1027
|
+
for (let index = 0; index < snapshot.dependencies.length; index++) {
|
|
1028
|
+
const edge = snapshot.dependencies[index]!, exact = dependencyKey(edge.dependentClassOid, edge.dependentOid, edge.dependentSubId, edge.referencedClassOid, edge.referencedOid, edge.referencedSubId, edge.kind), direct = directKey(edge.dependentClassOid, edge.dependentOid, edge.referencedClassOid, edge.referencedOid);
|
|
1029
|
+
const exactIndexes = dependencyIndexes.get(exact), directIndexes = directDependencyIndexes.get(direct);
|
|
1030
|
+
if (exactIndexes)
|
|
1031
|
+
exactIndexes.push(index);
|
|
1032
|
+
else
|
|
1033
|
+
dependencyIndexes.set(exact, [index]);
|
|
1034
|
+
if (directIndexes)
|
|
1035
|
+
directIndexes.push(index);
|
|
1036
|
+
else
|
|
1037
|
+
directDependencyIndexes.set(direct, [index]);
|
|
1038
|
+
}
|
|
1039
|
+
const consume = (dependentClassOid: string, dependentOid: string, dependentSubId: string, referencedClassOid: string, referencedOid: string, referencedSubId: string, kind: OwnedCatalogDependencyV1["kind"], count = 1): void => {
|
|
1040
|
+
const matches = dependencyIndexes.get(dependencyKey(dependentClassOid, dependentOid, dependentSubId, referencedClassOid, referencedOid, referencedSubId, kind)) ?? [];
|
|
1041
|
+
for (const index of matches)
|
|
1042
|
+
if (!remaining.delete(index))
|
|
1043
|
+
bad();
|
|
1044
|
+
if (matches.length !== count)
|
|
1045
|
+
bad();
|
|
1046
|
+
};
|
|
1047
|
+
const rejectDirect = (dependentClassOid: string, dependentOid: string, referencedClassOid: string, referencedOid: string): void => {
|
|
1048
|
+
if ((directDependencyIndexes.get(directKey(dependentClassOid, dependentOid, referencedClassOid, referencedOid)) ?? []).length)
|
|
1049
|
+
bad();
|
|
1050
|
+
};
|
|
1051
|
+
const columns = new Map<string, OwnedCatalogColumnV1[]>();
|
|
1052
|
+
for (const column of snapshot.columns) {
|
|
1053
|
+
const entries = columns.get(column.relationOid) ?? [];
|
|
1054
|
+
entries.push(column);
|
|
1055
|
+
columns.set(column.relationOid, entries);
|
|
1056
|
+
}
|
|
1057
|
+
const constraints = new Map<string, OwnedCatalogConstraintV1[]>();
|
|
1058
|
+
for (const constraint of snapshot.constraints) {
|
|
1059
|
+
const entries = constraints.get(constraint.relationOid) ?? [];
|
|
1060
|
+
entries.push(constraint);
|
|
1061
|
+
constraints.set(constraint.relationOid, entries);
|
|
1062
|
+
}
|
|
1063
|
+
const indexes = new Map<string, OwnedCatalogIndexV1[]>();
|
|
1064
|
+
for (const index of snapshot.indexes) {
|
|
1065
|
+
const entries = indexes.get(index.tableRelationOid) ?? [];
|
|
1066
|
+
entries.push(index);
|
|
1067
|
+
indexes.set(index.tableRelationOid, entries);
|
|
1068
|
+
}
|
|
1069
|
+
const consumedTriggerOids = new Set<string>();
|
|
1070
|
+
const sequences = new Map(snapshot.sequences.map((sequence) => [sequence.relationOid, sequence]));
|
|
1071
|
+
const consumedSequenceRelationOids = new Set<string>();
|
|
1072
|
+
const consumedToastRelationOids = new Set<string>();
|
|
1073
|
+
const declaredToastRelationOids = new Set(snapshot.relations.flatMap((relation) => relation.kind === "ordinaryTable" && relation.toastRelationOid !== null ? [relation.toastRelationOid] : []));
|
|
1074
|
+
const identityCandidates = new Map<string, OwnedCatalogSequenceV1[]>();
|
|
1075
|
+
for (const edge of snapshot.dependencies) {
|
|
1076
|
+
const sequence = sequences.get(edge.dependentOid);
|
|
1077
|
+
if (!sequence || edge.dependentClassOid !== pgClass || edge.dependentSubId !== "0" || edge.referencedClassOid !== pgClass || edge.kind !== "internal")
|
|
1078
|
+
continue;
|
|
1079
|
+
const key = JSON.stringify([edge.referencedOid, edge.referencedSubId]), candidates = identityCandidates.get(key);
|
|
1080
|
+
if (candidates)
|
|
1081
|
+
candidates.push(sequence);
|
|
1082
|
+
else
|
|
1083
|
+
identityCandidates.set(key, [sequence]);
|
|
1084
|
+
}
|
|
1085
|
+
const validateToast = (owner: OwnedCatalogRelationV1): void => validateToastClosureV1(owner, relations, columns, indexes, consumedToastRelationOids, pgClass, pgNamespace, consume, rejectDirect);
|
|
1086
|
+
for (const constraint of snapshot.constraints) {
|
|
1087
|
+
if (constraint.kind === "primaryKey") {
|
|
1088
|
+
const index = snapshot.indexes.find((entry) => entry.indexRelationOid === constraint.backingIndexOid);
|
|
1089
|
+
if (!index || index.name !== constraint.name || index.columnNames.length !== constraint.columns.length || index.columnNames.some((name, indexValue) => name !== constraint.columns[indexValue]))
|
|
1090
|
+
bad();
|
|
1091
|
+
}
|
|
1092
|
+
if (constraint.kind === "check") {
|
|
1093
|
+
const names = new Set<string>();
|
|
1094
|
+
if (constraint.checkExpression === null)
|
|
1095
|
+
bad();
|
|
1096
|
+
projectCheckAstIdentifiers(constraint.checkExpression, (name) => {
|
|
1097
|
+
names.add(name);
|
|
1098
|
+
return name;
|
|
1099
|
+
});
|
|
1100
|
+
if (constraint.columns.length !== names.size || constraint.columns.some((name) => !names.has(name)) || constraint.referencedColumns.length)
|
|
1101
|
+
bad();
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
const rowTypes = new Map(snapshot.rowTypes.map((rowType) => [rowType.relationOid, rowType]));
|
|
1105
|
+
const arrays = new Map(snapshot.arrayTypes.map((arrayType) => [arrayType.elementTypeOid, arrayType]));
|
|
1106
|
+
const tables = new Map<string, IntrospectedTable>();
|
|
1107
|
+
for (const relation of snapshot.relations) {
|
|
1108
|
+
if (relation.kind === "index" || relation.kind === "sequence" || relation.kind === "toastTable")
|
|
1109
|
+
continue;
|
|
1110
|
+
const key = tableKey(relation.schema, relation.name), expectedTable = expectedTables.get(key), scope = snapshot.requestedScopes.find((entry) => entry.schema === relation.schema && relation.name.startsWith(entry.tablePrefix));
|
|
1111
|
+
if (!expectedTable || !scope || !snapshot.existingSchemas.includes(relation.schema) || relation.kind !== "ordinaryTable" || relation.rawKind !== "r" || relation.persistence !== "permanent" || relation.isPartition || relation.rowSecurity || relation.forceRowSecurity || relation.replicaIdentity !== "default" || relation.tablespaceOid !== "0" || relation.accessMethod !== "heap" || relation.options.length || relation.rowTypeOid === null)
|
|
1112
|
+
bad();
|
|
1113
|
+
const rowType = rowTypes.get(relation.oid), arrayType = rowType ? arrays.get(rowType.oid) : undefined;
|
|
1114
|
+
if (!rowType || !arrayType || rowType.oid !== relation.rowTypeOid || rowType.schema !== relation.schema || rowType.name !== relation.name || rowType.kind !== "composite" || rowType.arrayTypeOid !== arrayType.oid || arrayType.schema !== relation.schema || arrayType.relationOid !== "0" || arrayType.arrayTypeOid !== "0" || arrayType.kind !== "base" || arrayType.category !== "array")
|
|
1115
|
+
bad();
|
|
1116
|
+
consume(pgType, rowType.oid, "0", pgClass, relation.oid, "0", "internal");
|
|
1117
|
+
consume(pgType, arrayType.oid, "0", pgType, rowType.oid, "0", "internal");
|
|
1118
|
+
if (relation.namespaceOid === "2200" || BigInt(relation.namespaceOid) >= 12000n)
|
|
1119
|
+
consume(pgClass, relation.oid, "0", pgNamespace, relation.namespaceOid, "0", "normal");
|
|
1120
|
+
else
|
|
1121
|
+
rejectDirect(pgClass, relation.oid, pgNamespace, relation.namespaceOid);
|
|
1122
|
+
validateToast(relation);
|
|
1123
|
+
const actualColumns = new Map<string, {
|
|
1124
|
+
readonly name: string;
|
|
1125
|
+
readonly notNull: boolean;
|
|
1126
|
+
readonly isPrimaryKey: boolean;
|
|
1127
|
+
readonly physicalType: string;
|
|
1128
|
+
readonly default: CanonicalDefault;
|
|
1129
|
+
readonly generation: "none" | "identityByDefault" | "uuidDefault";
|
|
1130
|
+
}>();
|
|
1131
|
+
const tableColumns = columns.get(relation.oid) ?? [];
|
|
1132
|
+
if (snapshot.columns.some((column) => !owned.has(column.relationOid) && !declaredToastRelationOids.has(column.relationOid)) || tableColumns.length !== expectedTable.columns.length)
|
|
1133
|
+
bad();
|
|
1134
|
+
const orderedColumns = [...tableColumns].sort((a, b) => Number(a.attnum) - Number(b.attnum));
|
|
1135
|
+
for (let index = 0; index < orderedColumns.length; index++) {
|
|
1136
|
+
const column = orderedColumns[index]!, modelColumn = expectedTable.columns[index]!;
|
|
1137
|
+
if (column.name !== modelColumn.column || column.attnum !== String(index + 1) || column.dropped || !column.local || column.inheritanceCount !== "0" || column.typeOid === "0" || BigInt(column.typeOid) >= 12000n || !["integer", "real", "text", "boolean", "datetime", "json", "uuid"].includes(column.physicalType) || column.collationOid !== column.typeDefaultCollationOid || column.storageCode !== column.typeDefaultStorageCode || column.compressionCode !== "" || column.generation === "other")
|
|
1138
|
+
bad();
|
|
1139
|
+
const expectedGeneration = modelColumn.generation;
|
|
1140
|
+
if (column.generation !== expectedGeneration || (column.generation === "none" && (column.identityCode !== "" || column.generatedCode !== "")) || (column.generation === "identityByDefault" && (column.identityCode !== "d" || column.generatedCode !== "")) || (column.generation === "uuidDefault" && (column.identityCode !== "" || column.generatedCode !== "")))
|
|
1141
|
+
bad();
|
|
1142
|
+
if (column.default.kind === "none") {
|
|
1143
|
+
if (column.defaultObjectOid !== null)
|
|
1144
|
+
bad();
|
|
1145
|
+
}
|
|
1146
|
+
else {
|
|
1147
|
+
if (column.defaultObjectOid === null)
|
|
1148
|
+
bad();
|
|
1149
|
+
consume(pgAttrdef, column.defaultObjectOid, "0", pgClass, relation.oid, column.attnum, "automatic");
|
|
1150
|
+
}
|
|
1151
|
+
if (expectedGeneration === "identityByDefault") {
|
|
1152
|
+
if (column.physicalType !== "integer" || column.default.kind !== "none" || column.defaultObjectOid !== null)
|
|
1153
|
+
bad();
|
|
1154
|
+
const candidates = identityCandidates.get(JSON.stringify([relation.oid, column.attnum])) ?? [];
|
|
1155
|
+
if (candidates.length !== 1)
|
|
1156
|
+
bad();
|
|
1157
|
+
const sequence = candidates[0]!, sequenceRelation = relations.get(sequence.relationOid);
|
|
1158
|
+
if (!sequenceRelation || sequenceRelation.kind !== "sequence" || sequenceRelation.rawKind !== "S" || sequenceRelation.persistence !== "permanent" || sequenceRelation.schema !== relation.schema || sequenceRelation.namespaceOid !== relation.namespaceOid || sequenceRelation.isPartition || sequenceRelation.rowSecurity || sequenceRelation.forceRowSecurity || sequenceRelation.replicaIdentity !== "nothing" || sequenceRelation.tablespaceOid !== "0" || sequenceRelation.accessMethod !== null || sequenceRelation.options.length || sequenceRelation.rowTypeOid !== null || sequenceRelation.toastRelationOid !== null || sequence.type !== "bigint" || sequence.start !== "1" || sequence.increment !== "1" || sequence.minimum !== "1" || sequence.maximum !== "9223372036854775807" || sequence.cache !== "1" || sequence.cycle || consumedSequenceRelationOids.has(sequence.relationOid))
|
|
1159
|
+
bad();
|
|
1160
|
+
consumedSequenceRelationOids.add(sequence.relationOid);
|
|
1161
|
+
consume(pgClass, sequence.relationOid, "0", pgClass, relation.oid, column.attnum, "internal");
|
|
1162
|
+
if (sequenceRelation.namespaceOid === "2200" || BigInt(sequenceRelation.namespaceOid) >= 12000n)
|
|
1163
|
+
consume(pgClass, sequenceRelation.oid, "0", pgNamespace, sequenceRelation.namespaceOid, "0", "normal");
|
|
1164
|
+
else
|
|
1165
|
+
rejectDirect(pgClass, sequenceRelation.oid, pgNamespace, sequenceRelation.namespaceOid);
|
|
1166
|
+
}
|
|
1167
|
+
actualColumns.set(column.name, {
|
|
1168
|
+
name: column.name, notNull: column.notNull, isPrimaryKey: false, physicalType: column.physicalType, default: column.default, generation: column.generation
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
const own = constraints.get(relation.oid) ?? [], primary = own.filter((constraint) => constraint.kind === "primaryKey");
|
|
1172
|
+
if (primary.length !== 1)
|
|
1173
|
+
bad();
|
|
1174
|
+
const pk = primary[0]!;
|
|
1175
|
+
if (pk.name !== expectedTable.primaryKey.name || pk.backingIndexOid === null || pk.referencedRelationOid !== null || !pk.noInherit || pk.parentConstraintOid !== null || pk.inheritanceCount !== "0" || !pk.validated || pk.deferrable || pk.initiallyDeferred || pk.checkExpression !== null || pk.referencedColumns.length || pk.onDelete !== null || pk.onUpdate !== null || pk.match !== null || pk.deleteSetColumns.length || pk.primaryForeignEqualityOperatorOids.length || pk.primaryPrimaryEqualityOperatorOids.length || pk.foreignForeignEqualityOperatorOids.length || pk.defaultEqualityOperatorOids.length || pk.columns.length !== expectedTable.primaryKey.columns.length || pk.columns.some((name, index) => name !== expectedTable.primaryKey.columns[index]))
|
|
1176
|
+
bad();
|
|
1177
|
+
for (const name of pk.columns) {
|
|
1178
|
+
const column = tableColumns.find((entry) => entry.name === name), actual = actualColumns.get(name);
|
|
1179
|
+
if (!column || !actual)
|
|
1180
|
+
bad();
|
|
1181
|
+
consume(pgConstraint, pk.oid, "0", pgClass, relation.oid, column.attnum, "automatic");
|
|
1182
|
+
actualColumns.set(name, { ...actual, isPrimaryKey: true });
|
|
1183
|
+
}
|
|
1184
|
+
const tableIndexes = indexes.get(relation.oid) ?? [], backing = tableIndexes.find((index) => index.indexRelationOid === pk.backingIndexOid);
|
|
1185
|
+
if (!backing || !backing.primary || !backing.unique || backing.backingConstraintOid !== pk.oid)
|
|
1186
|
+
bad();
|
|
1187
|
+
const validateIndex = (index: OwnedCatalogIndexV1, primary: boolean): readonly string[] => {
|
|
1188
|
+
const indexRelation = relations.get(index.indexRelationOid);
|
|
1189
|
+
if (!indexRelation || indexRelation.kind !== "index" || indexRelation.schema !== relation.schema || indexRelation.namespaceOid !== relation.namespaceOid || indexRelation.name !== index.name || index.method !== "btree" || index.primary !== primary || index.unique !== (primary || index.unique) || index.exclusion || !index.immediate || !index.valid || !index.ready || !index.live || index.replicaIdentity || index.nullsNotDistinct || index.expression !== null || index.predicate !== null || index.keyAttributeCount !== index.totalAttributeCount || index.attributeNumbers.length !== Number(index.keyAttributeCount) || index.columnNames.length !== Number(index.totalAttributeCount) || index.collationOids.length !== Number(index.keyAttributeCount) || index.opclassOids.length !== Number(index.keyAttributeCount) || index.defaultOpclassOids.length !== Number(index.keyAttributeCount) || index.options.length !== Number(index.keyAttributeCount) || index.attributeNumbers.some((value, position) => value !== String(tableColumns.find((column) => column.name === index.columnNames[position])?.attnum ?? "")) || index.columnNames.some((name) => name === null) || index.options.some((value) => value !== "0") || index.opclassOids.some((value, position) => value !== index.defaultOpclassOids[position] || BigInt(value) >= 12000n) || index.collationOids.some((value, position) => value !== tableColumns.find((column) => column.name === index.columnNames[position])?.typeDefaultCollationOid))
|
|
1190
|
+
bad();
|
|
1191
|
+
return index.columnNames as readonly string[];
|
|
1192
|
+
};
|
|
1193
|
+
validateIndex(backing, true);
|
|
1194
|
+
consume(pgClass, backing.indexRelationOid, "0", pgConstraint, pk.oid, "0", "internal");
|
|
1195
|
+
const ordinary = tableIndexes.filter((index) => index.indexRelationOid !== backing.indexRelationOid).map((index) => {
|
|
1196
|
+
if (index.backingConstraintOid !== null)
|
|
1197
|
+
bad();
|
|
1198
|
+
const names = validateIndex(index, false);
|
|
1199
|
+
for (const name of names) {
|
|
1200
|
+
const column = tableColumns.find((entry) => entry.name === name);
|
|
1201
|
+
if (!column)
|
|
1202
|
+
bad();
|
|
1203
|
+
consume(pgClass, index.indexRelationOid, "0", pgClass, relation.oid, column.attnum, "automatic");
|
|
1204
|
+
}
|
|
1205
|
+
return { name: index.name, columns: names, unique: index.unique, method: "btree" as const };
|
|
1206
|
+
});
|
|
1207
|
+
const foreignKeys = own.filter((constraint) => constraint.kind === "foreignKey"), checks = own.filter((constraint) => constraint.kind === "check");
|
|
1208
|
+
if (own.length !== 1 + foreignKeys.length + checks.length || snapshot.rules.some((rule) => rule.relationOid === relation.oid) || snapshot.policies.some((policy) => policy.relationOid === relation.oid) || snapshot.inheritance.some((entry) => entry.childRelationOid === relation.oid || entry.parentRelationOid === relation.oid))
|
|
1209
|
+
bad();
|
|
1210
|
+
for (const constraint of foreignKeys) {
|
|
1211
|
+
if (constraint.referencedRelationOid === null || !constraint.noInherit || constraint.parentConstraintOid !== null || constraint.inheritanceCount !== "0" || !constraint.validated || constraint.deferrable || constraint.initiallyDeferred || constraint.match !== "simple" || constraint.deleteSetColumns.length || constraint.backingIndexOid === null || constraint.checkExpression !== null)
|
|
1212
|
+
bad();
|
|
1213
|
+
const target = relations.get(constraint.referencedRelationOid);
|
|
1214
|
+
if (!target || !owned.has(target.oid) || target.kind !== "ordinaryTable")
|
|
1215
|
+
bad();
|
|
1216
|
+
const targetPrimary = (constraints.get(target.oid) ?? []).filter((entry) => entry.kind === "primaryKey");
|
|
1217
|
+
if (targetPrimary.length !== 1)
|
|
1218
|
+
bad();
|
|
1219
|
+
const targetPk = targetPrimary[0]!, targetColumns = columns.get(target.oid) ?? [];
|
|
1220
|
+
const targetIndex = (indexes.get(target.oid) ?? []).find((entry) => entry.indexRelationOid === targetPk.backingIndexOid);
|
|
1221
|
+
if (targetPk.backingIndexOid === null || constraint.backingIndexOid !== targetPk.backingIndexOid || !targetIndex || targetIndex.tableRelationOid !== target.oid || targetIndex.columnNames.length !== targetPk.columns.length || targetIndex.columnNames.some((name, position) => name !== targetPk.columns[position]) || targetIndex.attributeNumbers.some((attnum, position) => attnum !== targetColumns.find((column) => column.name === targetPk.columns[position])?.attnum))
|
|
1222
|
+
bad();
|
|
1223
|
+
const vectors = [
|
|
1224
|
+
constraint.primaryForeignEqualityOperatorOids, constraint.primaryPrimaryEqualityOperatorOids, constraint.foreignForeignEqualityOperatorOids, constraint.defaultEqualityOperatorOids
|
|
1225
|
+
];
|
|
1226
|
+
if (!constraint.columns.length || constraint.referencedColumns.length !== constraint.columns.length || vectors.some((vector) => vector.length !== constraint.columns.length) || constraint.referencedColumns.some((name, position) => name !== targetPk.columns[position]))
|
|
1227
|
+
bad();
|
|
1228
|
+
for (let position = 0; position < constraint.columns.length; position++) {
|
|
1229
|
+
const defaultOperator = constraint.defaultEqualityOperatorOids[position]!;
|
|
1230
|
+
if (BigInt(defaultOperator) >= 12000n || constraint.primaryForeignEqualityOperatorOids[position] !== defaultOperator || constraint.primaryPrimaryEqualityOperatorOids[position] !== defaultOperator || constraint.foreignForeignEqualityOperatorOids[position] !== defaultOperator)
|
|
1231
|
+
bad();
|
|
1232
|
+
}
|
|
1233
|
+
const sourceKeys = new Set<string>(), targetKeys = new Set<string>();
|
|
1234
|
+
for (const name of constraint.columns) {
|
|
1235
|
+
const column = tableColumns.find((entry) => entry.name === name);
|
|
1236
|
+
if (!column)
|
|
1237
|
+
bad();
|
|
1238
|
+
sourceKeys.add(column.attnum);
|
|
1239
|
+
}
|
|
1240
|
+
for (const name of constraint.referencedColumns) {
|
|
1241
|
+
const column = targetColumns.find((entry) => entry.name === name);
|
|
1242
|
+
if (!column)
|
|
1243
|
+
bad();
|
|
1244
|
+
targetKeys.add(column.attnum);
|
|
1245
|
+
}
|
|
1246
|
+
for (const attnum of sourceKeys)
|
|
1247
|
+
consume(pgConstraint, constraint.oid, "0", pgClass, relation.oid, attnum, "automatic");
|
|
1248
|
+
for (const attnum of targetKeys)
|
|
1249
|
+
consume(pgConstraint, constraint.oid, "0", pgClass, target.oid, attnum, "normal");
|
|
1250
|
+
consume(pgConstraint, constraint.oid, "0", pgClass, targetIndex.indexRelationOid, "0", "normal");
|
|
1251
|
+
const pgTrigger = classOid("pg_trigger", "pg_trigger"), pgProc = classOid("pg_proc", "pg_proc");
|
|
1252
|
+
const actionNames = new Map<string, string>([["noAction", "noaction"], ["restrict", "restrict"], ["cascade", "cascade"], ["setNull", "setnull"]]);
|
|
1253
|
+
const deleteAction = constraint.onDelete === null ? undefined : actionNames.get(constraint.onDelete), updateAction = constraint.onUpdate === null ? undefined : actionNames.get(constraint.onUpdate);
|
|
1254
|
+
if (!deleteAction || !updateAction)
|
|
1255
|
+
bad();
|
|
1256
|
+
const roles = [
|
|
1257
|
+
{ relationOid: relation.oid, functionName: "RI_FKey_check_ins", typeBits: "5" },
|
|
1258
|
+
{ relationOid: relation.oid, functionName: "RI_FKey_check_upd", typeBits: "17" },
|
|
1259
|
+
{ relationOid: target.oid, functionName: `RI_FKey_${deleteAction}_del`, typeBits: "9" },
|
|
1260
|
+
{ relationOid: target.oid, functionName: `RI_FKey_${updateAction}_upd`, typeBits: "17" },
|
|
1261
|
+
];
|
|
1262
|
+
const foreignKeyTriggers = snapshot.triggers.filter((trigger) => trigger.constraintOid === constraint.oid);
|
|
1263
|
+
if (foreignKeyTriggers.length !== roles.length)
|
|
1264
|
+
bad();
|
|
1265
|
+
for (const role of roles) {
|
|
1266
|
+
const matches = foreignKeyTriggers.filter((trigger) => trigger.relationOid === role.relationOid && trigger.internal && trigger.parentTriggerOid === null && trigger.enabled === "origin" && trigger.functionSchema === "pg_catalog" && trigger.functionName === role.functionName && trigger.typeBits === role.typeBits && BigInt(trigger.functionOid) < 12000n);
|
|
1267
|
+
if (matches.length !== 1)
|
|
1268
|
+
bad();
|
|
1269
|
+
const trigger = matches[0]!;
|
|
1270
|
+
if (consumedTriggerOids.has(trigger.oid))
|
|
1271
|
+
bad();
|
|
1272
|
+
consumedTriggerOids.add(trigger.oid);
|
|
1273
|
+
consume(pgTrigger, trigger.oid, "0", pgConstraint, constraint.oid, "0", "internal");
|
|
1274
|
+
rejectDirect(pgTrigger, trigger.oid, pgProc, trigger.functionOid);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
for (const constraint of checks) {
|
|
1278
|
+
if (constraint.referencedRelationOid !== null || constraint.backingIndexOid !== null || constraint.noInherit || constraint.parentConstraintOid !== null || constraint.inheritanceCount !== "0" || !constraint.validated || constraint.deferrable || constraint.initiallyDeferred || constraint.onDelete !== null || constraint.onUpdate !== null || constraint.match !== null || constraint.deleteSetColumns.length || constraint.primaryForeignEqualityOperatorOids.length || constraint.primaryPrimaryEqualityOperatorOids.length || constraint.foreignForeignEqualityOperatorOids.length || constraint.defaultEqualityOperatorOids.length || constraint.checkExpression === null)
|
|
1279
|
+
bad();
|
|
1280
|
+
const names = new Set<string>();
|
|
1281
|
+
projectCheckAstIdentifiers(constraint.checkExpression, (name) => {
|
|
1282
|
+
names.add(name);
|
|
1283
|
+
return name;
|
|
1284
|
+
});
|
|
1285
|
+
if (constraint.columns.length !== names.size || constraint.columns.some((name) => !names.has(name)))
|
|
1286
|
+
bad();
|
|
1287
|
+
for (const name of names) {
|
|
1288
|
+
const column = tableColumns.find((entry) => entry.name === name);
|
|
1289
|
+
if (!column)
|
|
1290
|
+
bad();
|
|
1291
|
+
consume(pgConstraint, constraint.oid, "0", pgClass, relation.oid, column.attnum, "automatic");
|
|
1292
|
+
consume(pgConstraint, constraint.oid, "0", pgClass, relation.oid, column.attnum, "normal");
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
tables.set(key, {
|
|
1296
|
+
name: relation.name, columns: actualColumns, primaryKey: { name: pk.name, columns: pk.columns }, indexes: ordinary, foreignKeys: foreignKeys.map((constraint) => {
|
|
1297
|
+
const target = relations.get(constraint.referencedRelationOid!);
|
|
1298
|
+
if (!target)
|
|
1299
|
+
bad();
|
|
1300
|
+
return {
|
|
1301
|
+
name: constraint.name, columns: constraint.columns, targetSchema: target.schema, targetTable: target.name, targetColumns: constraint.referencedColumns, onDelete: constraint.onDelete ?? "", onUpdate: constraint.onUpdate ?? ""
|
|
1302
|
+
};
|
|
1303
|
+
}), checks: checks.map((constraint) => ({ name: constraint.name, expression: constraint.checkExpression! }))
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
for (const relation of snapshot.relations.filter((entry) => entry.kind === "index")) {
|
|
1307
|
+
const index = snapshot.indexes.find((entry) => entry.indexRelationOid === relation.oid);
|
|
1308
|
+
if (!index || relation.rawKind !== "i" || relation.persistence !== "permanent" || relation.isPartition || relation.rowSecurity || relation.forceRowSecurity || relation.replicaIdentity !== "nothing" || relation.tablespaceOid !== "0" || relation.accessMethod !== "btree" || relation.options.length || relation.rowTypeOid !== null || relation.toastRelationOid !== null)
|
|
1309
|
+
bad();
|
|
1310
|
+
rejectDirect(pgClass, relation.oid, pgNamespace, relation.namespaceOid);
|
|
1311
|
+
}
|
|
1312
|
+
if (rowTypes.size !== owned.size || arrays.size !== owned.size || snapshot.rowTypes.some((rowType) => !owned.has(rowType.relationOid)) || snapshot.arrayTypes.some((arrayType) => !snapshot.rowTypes.some((rowType) => rowType.oid === arrayType.elementTypeOid)) || snapshot.columns.some((column) => !owned.has(column.relationOid) && !consumedToastRelationOids.has(column.relationOid)) || snapshot.indexes.some((index) => !owned.has(index.tableRelationOid) && !consumedToastRelationOids.has(index.tableRelationOid)) || snapshot.constraints.some((constraint) => !owned.has(constraint.relationOid)) || snapshot.rules.some((rule) => !owned.has(rule.relationOid)) || snapshot.policies.some((policy) => !owned.has(policy.relationOid)) || snapshot.inheritance.length || snapshot.relations.some((relation) => relation.kind !== "ordinaryTable" && relation.kind !== "index" && (relation.kind !== "sequence" || !consumedSequenceRelationOids.has(relation.oid)) && (relation.kind !== "toastTable" || !consumedToastRelationOids.has(relation.oid))) || tables.size !== expectedTables.size || consumedTriggerOids.size !== snapshot.triggers.length || consumedSequenceRelationOids.size !== snapshot.sequences.length || consumedToastRelationOids.size !== declaredToastRelationOids.size || remaining.size)
|
|
1313
|
+
bad();
|
|
1314
|
+
for (const store of modelStores) {
|
|
1315
|
+
const projection = new Map<string, IntrospectedTable>();
|
|
1316
|
+
for (const relation of analysis.rootsByStoreKey.get(store.definition.storeKey) ?? []) {
|
|
1317
|
+
const key = `${relation.schema}.${relation.name}`, table = tables.get(tableKey(relation.schema, relation.name));
|
|
1318
|
+
if (!table)
|
|
1319
|
+
bad();
|
|
1320
|
+
projection.set(key, table);
|
|
1321
|
+
}
|
|
1322
|
+
if (!new ExactSchemaVerifier().verify(store.expectedSchema, { tables: projection }, false).compatible)
|
|
1323
|
+
bad();
|
|
1324
|
+
}
|
|
1325
|
+
}
|