@rocicorp/zero 0.26.1-canary.11 → 0.26.1-canary.12

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.
Files changed (45) hide show
  1. package/out/analyze-query/src/bin-analyze.js +3 -0
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/analyze-query/src/run-ast.d.ts.map +1 -1
  4. package/out/analyze-query/src/run-ast.js +11 -2
  5. package/out/analyze-query/src/run-ast.js.map +1 -1
  6. package/out/zero/package.json.js +1 -1
  7. package/out/zero-cache/src/db/lite-tables.d.ts +2 -1
  8. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  9. package/out/zero-cache/src/db/lite-tables.js +7 -3
  10. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  11. package/out/zero-cache/src/db/specs.d.ts +8 -2
  12. package/out/zero-cache/src/db/specs.d.ts.map +1 -1
  13. package/out/zero-cache/src/db/specs.js.map +1 -1
  14. package/out/zero-cache/src/services/analyze.js +1 -0
  15. package/out/zero-cache/src/services/analyze.js.map +1 -1
  16. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +1 -0
  17. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  18. package/out/zero-cache/src/services/change-source/common/replica-schema.js +32 -3
  19. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  20. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  21. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +15 -11
  22. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  23. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  24. package/out/zero-cache/src/services/change-source/pg/change-source.js +61 -0
  25. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  26. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  27. package/out/zero-cache/src/services/replicator/change-processor.js +10 -13
  28. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  29. package/out/zero-cache/src/services/replicator/schema/table-metadata.d.ts +25 -7
  30. package/out/zero-cache/src/services/replicator/schema/table-metadata.d.ts.map +1 -1
  31. package/out/zero-cache/src/services/replicator/schema/table-metadata.js +54 -24
  32. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  33. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  34. package/out/zero-cache/src/services/run-ast.js +4 -2
  35. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  36. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +3 -2
  37. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  38. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +27 -12
  39. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  40. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +3 -3
  41. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  42. package/out/zero-cache/src/services/view-syncer/snapshotter.js +4 -0
  43. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  44. package/out/zero-client/src/client/version.js +1 -1
  45. package/package.json +2 -1
@@ -207,6 +207,7 @@ if (config.ast) {
207
207
  clientToServerMapper,
208
208
  permissions,
209
209
  syncedRows: config.outputSyncedRows,
210
+ db,
210
211
  host
211
212
  });
212
213
  } else if (config.query) {
@@ -235,6 +236,7 @@ function runQuery(queryString) {
235
236
  clientToServerMapper,
236
237
  permissions,
237
238
  syncedRows: config.outputSyncedRows,
239
+ db,
238
240
  host
239
241
  });
240
242
  }
@@ -256,6 +258,7 @@ async function runHash(hash) {
256
258
  clientToServerMapper,
257
259
  permissions,
258
260
  syncedRows: config.outputSyncedRows,
261
+ db,
259
262
  host
260
263
  });
261
264
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bin-analyze.js","sources":["../../../../analyze-query/src/bin-analyze.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport chalk from 'chalk';\nimport fs from 'node:fs';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {testLogConfig} from '../../otel/src/test-log-config.ts';\nimport {colorConsole, createLogContext} from '../../shared/src/logging.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n zeroOptions,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {\n computeZqlSpecs,\n mustGetTableSpec,\n} from '../../zero-cache/src/db/lite-tables.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from '../../zero-cache/src/scripts/permissions.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport {type AST} from '../../zero-protocol/src/ast.ts';\nimport {clientSchemaFrom} from '../../zero-schema/src/builder/schema-builder.ts';\nimport {clientToServer} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../zql/src/builder/debug-delegate.ts';\nimport type {Source} from '../../zql/src/ivm/source.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {newQuery} from '../../zql/src/query/query-impl.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport type {PullRow, Query} from '../../zql/src/query/query.ts';\nimport {Database} from '../../zqlite/src/db.ts';\nimport {TableSource} from '../../zqlite/src/table-source.ts';\nimport {explainQueries} from './explain-queries.ts';\nimport {runAst} from './run-ast.ts';\n\nconst options = {\n schema: deployPermissionsOptions.schema,\n replicaFile: {\n ...zeroOptions.replica.file,\n desc: [`File path to the SQLite replica to test queries against.`],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'AST for the query to be analyzed. Only one of ast/query/hash should be provided.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n `Query to be analyzed in the form of: table.where(...).related(...).etc. `,\n `Only one of ast/query/hash should be provided.`,\n ],\n },\n hash: {\n type: v.string().optional(),\n desc: [\n `Hash of the query to be analyzed. This is used to look up the query in the database. `,\n `Only one of ast/query/hash should be provided.`,\n `You should run this script from the directory containing your .env file to reduce the amount of`,\n `configuration required. The .env file should contain the connection URL to the CVR database.`,\n ],\n },\n applyPermissions: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to apply permissions (from your schema file) to the provided query.',\n ],\n },\n authData: {\n type: v.string().optional(),\n desc: [\n 'JSON encoded payload of the auth data.',\n 'This will be used to fill permission variables if the \"applyPermissions\" option is set',\n ],\n },\n outputVendedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which were read from the replica in order to execute the analyzed query. ',\n 'If the same row is read more than once it will be logged once for each time it was read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which would be synced to the client for the analyzed query.',\n ],\n },\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n 'Connection URL to the CVR database. If using --hash, either this or --upstream-db',\n 'must be specified.',\n ],\n },\n },\n upstream: {\n db: {\n desc: [\n `Connection URL to the \"upstream\" authoritative postgres database. If using --hash, `,\n 'either this or --cvr-db must be specified.',\n ],\n type: v.string().optional(),\n },\n type: zeroOptions.upstream.type,\n },\n app: appOptions,\n shard: shardOptions,\n log: {\n ...logOptions,\n level: logLevel.default('error'),\n },\n};\n\nconst cfg = parseOptions(options, {\n // the command line parses drops all text after the first newline\n // so we need to replace newlines with spaces\n // before parsing\n argv: process.argv.slice(2).map(s => s.replaceAll('\\n', ' ')),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query',\n content: `Analyze a ZQL query and show information about how it runs against a SQLite replica.\n\n analyze-query uses the same environment variables and flags as zero-cache-dev. If run from your development environment, it will pick up your ZERO_REPLICA_FILE, ZERO_SCHEMA_PATH, and other env vars automatically.\n\n If run in another environment (e.g., production) you will have to specify these flags. In particular, you must have a copy of the appropriate Zero schema file to give to the --schema-path flag.`,\n },\n {\n header: 'Examples',\n content: `# In development\n npx analyze-query --query='issue.related(\"comments\").limit(10)'\n npx analyze-query --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n npx analyze-query --hash=1234567890\n\n # In production\n # First copy schema.ts to your production environment, then run:\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n\n # cvr-db is required when using the hash option.\n # It is typically the same as your upstream db.\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --cvr-db='postgres://user:pass@host:port/db' \\\\\n --hash=1234567890\n `,\n },\n ],\n});\nconst config = {\n ...cfg,\n cvr: {\n ...cfg.cvr,\n db: cfg.cvr.db ?? cfg.upstream.db,\n },\n};\n\nruntimeDebugFlags.trackRowCountsVended = true;\nruntimeDebugFlags.trackRowsVended = config.outputVendedRows;\n\nconst lc = createLogContext({\n log: config.log,\n});\n\nif (!fs.existsSync(config.replicaFile)) {\n colorConsole.error(`Replica file ${config.replicaFile} does not exist`);\n process.exit(1);\n}\nconst db = new Database(lc, config.replicaFile);\n\nconst {schema, permissions} = await loadSchemaAndPermissions(\n config.schema.path,\n);\nconst clientSchema = clientSchemaFrom(schema).clientSchema;\n\nconst sources = new Map<string, TableSource>();\nconst clientToServerMapper = clientToServer(schema.tables);\nconst debug = new Debug();\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n\n getSource(serverTableName: string): Source | undefined {\n let source = sources.get(serverTableName);\n if (source) {\n return source;\n }\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n testLogConfig,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n\n sources.set(serverTableName, source);\n return source;\n }\n}\n\nconst host = new AnalyzeQueryDelegate();\n\nlet result: AnalyzeQueryResult;\n\nif (config.ast) {\n // the user likely has a transformed AST since the wire and storage formats are the transformed AST\n result = await runAst(lc, clientSchema, JSON.parse(config.ast), true, {\n applyPermissions: config.applyPermissions,\n authData: config.authData,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n });\n} else if (config.query) {\n result = await runQuery(config.query);\n} else if (config.hash) {\n result = await runHash(config.hash);\n} else {\n colorConsole.error('No query or AST or hash provided');\n process.exit(1);\n}\n\nfunction runQuery(queryString: string): Promise<AnalyzeQueryResult> {\n const z = {\n query: Object.fromEntries(\n Object.entries(schema.tables).map(([name]) => [\n name,\n newQuery(schema, name),\n ]),\n ),\n };\n\n const f = new Function('z', `return z.query.${queryString};`);\n const q: Query<string, Schema, PullRow<string, Schema>> = f(z);\n\n const ast = asQueryInternals(q).ast;\n return runAst(lc, clientSchema, ast, false, {\n applyPermissions: config.applyPermissions,\n authData: config.authData,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n });\n}\n\nasync function runHash(hash: string) {\n const cvrDB = pgClient(\n lc,\n must(config.cvr.db, 'CVR DB must be provided when using the hash option'),\n );\n\n const rows = await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(\n upstreamSchema(getShardID(config)) + '/cvr',\n )}.\"queries\" where \"queryHash\" = ${must(hash)} limit 1;`;\n await cvrDB.end();\n\n colorConsole.log('ZQL from Hash:');\n const ast = rows[0].clientAST as AST;\n colorConsole.log(await formatOutput(ast.table + astToZQL(ast)));\n\n return runAst(lc, clientSchema, ast, true, {\n applyPermissions: config.applyPermissions,\n authData: config.authData,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n });\n}\n\nif (config.outputSyncedRows) {\n colorConsole.log(chalk.blue.bold('=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(chalk.bold(table + ':'), rows);\n }\n}\n\ncolorConsole.log(chalk.blue.bold('=== Query Stats: ===\\n'));\ncolorConsole.log(chalk.bold('total synced rows:'), result.syncedRowCount);\nshowStats();\nif (config.outputVendedRows) {\n colorConsole.log(chalk.blue.bold('=== JS Row Scan Values: ===\\n'));\n for (const source of sources.values()) {\n colorConsole.log(\n chalk.bold(`${source.tableSchema.name}:`),\n debug.getVendedRows()?.[source.tableSchema.name] ?? {},\n );\n }\n}\n\ncolorConsole.log(chalk.blue.bold('\\n=== Rows Scanned (by SQLite): ===\\n'));\nconst nvisitCounts = debug.getNVisitCounts();\nlet totalNVisit = 0;\nfor (const [table, queries] of Object.entries(nvisitCounts)) {\n colorConsole.log(chalk.bold(`${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n}\ncolorConsole.log(\n chalk.bold('total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n);\n\ncolorConsole.log(chalk.blue.bold('\\n\\n=== Query Plans: ===\\n'));\nconst plans = explainQueries(debug.getVendedRowCounts() ?? {}, db);\nfor (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(chalk.bold('query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n}\n\nfunction showStats() {\n let totalRowsConsidered = 0;\n for (const source of sources.values()) {\n const values = Object.values(\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n for (const v of values) {\n totalRowsConsidered += v;\n }\n colorConsole.log(\n chalk.bold(source.tableSchema.name + ' vended:'),\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n }\n\n colorConsole.log(\n chalk.bold('Rows Read (into JS):'),\n colorRowsConsidered(totalRowsConsidered),\n );\n colorConsole.log(\n chalk.bold('time:'),\n colorTime(result.end - result.start),\n 'ms',\n );\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return chalk.green(duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return chalk.yellow(duration.toFixed(2) + 'ms');\n }\n return chalk.red(duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return chalk.green(n.toString());\n } else if (n < 10000) {\n return chalk.yellow(n.toString());\n }\n return chalk.red(n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return chalk.yellow(row);\n }\n return chalk.red(row);\n }\n return chalk.green(row);\n}\n"],"names":["v.string","v.boolean","v"],"mappings":"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"}
1
+ {"version":3,"file":"bin-analyze.js","sources":["../../../../analyze-query/src/bin-analyze.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport chalk from 'chalk';\nimport fs from 'node:fs';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {testLogConfig} from '../../otel/src/test-log-config.ts';\nimport {colorConsole, createLogContext} from '../../shared/src/logging.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n zeroOptions,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {\n computeZqlSpecs,\n mustGetTableSpec,\n} from '../../zero-cache/src/db/lite-tables.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from '../../zero-cache/src/scripts/permissions.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport {type AST} from '../../zero-protocol/src/ast.ts';\nimport {clientSchemaFrom} from '../../zero-schema/src/builder/schema-builder.ts';\nimport {clientToServer} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../zql/src/builder/debug-delegate.ts';\nimport type {Source} from '../../zql/src/ivm/source.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {newQuery} from '../../zql/src/query/query-impl.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport type {PullRow, Query} from '../../zql/src/query/query.ts';\nimport {Database} from '../../zqlite/src/db.ts';\nimport {TableSource} from '../../zqlite/src/table-source.ts';\nimport {explainQueries} from './explain-queries.ts';\nimport {runAst} from './run-ast.ts';\n\nconst options = {\n schema: deployPermissionsOptions.schema,\n replicaFile: {\n ...zeroOptions.replica.file,\n desc: [`File path to the SQLite replica to test queries against.`],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'AST for the query to be analyzed. Only one of ast/query/hash should be provided.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n `Query to be analyzed in the form of: table.where(...).related(...).etc. `,\n `Only one of ast/query/hash should be provided.`,\n ],\n },\n hash: {\n type: v.string().optional(),\n desc: [\n `Hash of the query to be analyzed. This is used to look up the query in the database. `,\n `Only one of ast/query/hash should be provided.`,\n `You should run this script from the directory containing your .env file to reduce the amount of`,\n `configuration required. The .env file should contain the connection URL to the CVR database.`,\n ],\n },\n applyPermissions: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to apply permissions (from your schema file) to the provided query.',\n ],\n },\n authData: {\n type: v.string().optional(),\n desc: [\n 'JSON encoded payload of the auth data.',\n 'This will be used to fill permission variables if the \"applyPermissions\" option is set',\n ],\n },\n outputVendedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which were read from the replica in order to execute the analyzed query. ',\n 'If the same row is read more than once it will be logged once for each time it was read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().default(false),\n desc: [\n 'Whether to output the rows which would be synced to the client for the analyzed query.',\n ],\n },\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n 'Connection URL to the CVR database. If using --hash, either this or --upstream-db',\n 'must be specified.',\n ],\n },\n },\n upstream: {\n db: {\n desc: [\n `Connection URL to the \"upstream\" authoritative postgres database. If using --hash, `,\n 'either this or --cvr-db must be specified.',\n ],\n type: v.string().optional(),\n },\n type: zeroOptions.upstream.type,\n },\n app: appOptions,\n shard: shardOptions,\n log: {\n ...logOptions,\n level: logLevel.default('error'),\n },\n};\n\nconst cfg = parseOptions(options, {\n // the command line parses drops all text after the first newline\n // so we need to replace newlines with spaces\n // before parsing\n argv: process.argv.slice(2).map(s => s.replaceAll('\\n', ' ')),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query',\n content: `Analyze a ZQL query and show information about how it runs against a SQLite replica.\n\n analyze-query uses the same environment variables and flags as zero-cache-dev. If run from your development environment, it will pick up your ZERO_REPLICA_FILE, ZERO_SCHEMA_PATH, and other env vars automatically.\n\n If run in another environment (e.g., production) you will have to specify these flags. In particular, you must have a copy of the appropriate Zero schema file to give to the --schema-path flag.`,\n },\n {\n header: 'Examples',\n content: `# In development\n npx analyze-query --query='issue.related(\"comments\").limit(10)'\n npx analyze-query --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n npx analyze-query --hash=1234567890\n\n # In production\n # First copy schema.ts to your production environment, then run:\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n\n # cvr-db is required when using the hash option.\n # It is typically the same as your upstream db.\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --cvr-db='postgres://user:pass@host:port/db' \\\\\n --hash=1234567890\n `,\n },\n ],\n});\nconst config = {\n ...cfg,\n cvr: {\n ...cfg.cvr,\n db: cfg.cvr.db ?? cfg.upstream.db,\n },\n};\n\nruntimeDebugFlags.trackRowCountsVended = true;\nruntimeDebugFlags.trackRowsVended = config.outputVendedRows;\n\nconst lc = createLogContext({\n log: config.log,\n});\n\nif (!fs.existsSync(config.replicaFile)) {\n colorConsole.error(`Replica file ${config.replicaFile} does not exist`);\n process.exit(1);\n}\nconst db = new Database(lc, config.replicaFile);\n\nconst {schema, permissions} = await loadSchemaAndPermissions(\n config.schema.path,\n);\nconst clientSchema = clientSchemaFrom(schema).clientSchema;\n\nconst sources = new Map<string, TableSource>();\nconst clientToServerMapper = clientToServer(schema.tables);\nconst debug = new Debug();\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n\n getSource(serverTableName: string): Source | undefined {\n let source = sources.get(serverTableName);\n if (source) {\n return source;\n }\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n testLogConfig,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n\n sources.set(serverTableName, source);\n return source;\n }\n}\n\nconst host = new AnalyzeQueryDelegate();\n\nlet result: AnalyzeQueryResult;\n\nif (config.ast) {\n // the user likely has a transformed AST since the wire and storage formats are the transformed AST\n result = await runAst(lc, clientSchema, JSON.parse(config.ast), true, {\n applyPermissions: config.applyPermissions,\n authData: config.authData,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n });\n} else if (config.query) {\n result = await runQuery(config.query);\n} else if (config.hash) {\n result = await runHash(config.hash);\n} else {\n colorConsole.error('No query or AST or hash provided');\n process.exit(1);\n}\n\nfunction runQuery(queryString: string): Promise<AnalyzeQueryResult> {\n const z = {\n query: Object.fromEntries(\n Object.entries(schema.tables).map(([name]) => [\n name,\n newQuery(schema, name),\n ]),\n ),\n };\n\n const f = new Function('z', `return z.query.${queryString};`);\n const q: Query<string, Schema, PullRow<string, Schema>> = f(z);\n\n const ast = asQueryInternals(q).ast;\n return runAst(lc, clientSchema, ast, false, {\n applyPermissions: config.applyPermissions,\n authData: config.authData,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n });\n}\n\nasync function runHash(hash: string) {\n const cvrDB = pgClient(\n lc,\n must(config.cvr.db, 'CVR DB must be provided when using the hash option'),\n );\n\n const rows = await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(\n upstreamSchema(getShardID(config)) + '/cvr',\n )}.\"queries\" where \"queryHash\" = ${must(hash)} limit 1;`;\n await cvrDB.end();\n\n colorConsole.log('ZQL from Hash:');\n const ast = rows[0].clientAST as AST;\n colorConsole.log(await formatOutput(ast.table + astToZQL(ast)));\n\n return runAst(lc, clientSchema, ast, true, {\n applyPermissions: config.applyPermissions,\n authData: config.authData,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n });\n}\n\nif (config.outputSyncedRows) {\n colorConsole.log(chalk.blue.bold('=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(chalk.bold(table + ':'), rows);\n }\n}\n\ncolorConsole.log(chalk.blue.bold('=== Query Stats: ===\\n'));\ncolorConsole.log(chalk.bold('total synced rows:'), result.syncedRowCount);\nshowStats();\nif (config.outputVendedRows) {\n colorConsole.log(chalk.blue.bold('=== JS Row Scan Values: ===\\n'));\n for (const source of sources.values()) {\n colorConsole.log(\n chalk.bold(`${source.tableSchema.name}:`),\n debug.getVendedRows()?.[source.tableSchema.name] ?? {},\n );\n }\n}\n\ncolorConsole.log(chalk.blue.bold('\\n=== Rows Scanned (by SQLite): ===\\n'));\nconst nvisitCounts = debug.getNVisitCounts();\nlet totalNVisit = 0;\nfor (const [table, queries] of Object.entries(nvisitCounts)) {\n colorConsole.log(chalk.bold(`${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n}\ncolorConsole.log(\n chalk.bold('total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n);\n\ncolorConsole.log(chalk.blue.bold('\\n\\n=== Query Plans: ===\\n'));\nconst plans = explainQueries(debug.getVendedRowCounts() ?? {}, db);\nfor (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(chalk.bold('query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n}\n\nfunction showStats() {\n let totalRowsConsidered = 0;\n for (const source of sources.values()) {\n const values = Object.values(\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n for (const v of values) {\n totalRowsConsidered += v;\n }\n colorConsole.log(\n chalk.bold(source.tableSchema.name + ' vended:'),\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n }\n\n colorConsole.log(\n chalk.bold('Rows Read (into JS):'),\n colorRowsConsidered(totalRowsConsidered),\n );\n colorConsole.log(\n chalk.bold('time:'),\n colorTime(result.end - result.start),\n 'ms',\n );\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return chalk.green(duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return chalk.yellow(duration.toFixed(2) + 'ms');\n }\n return chalk.red(duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return chalk.green(n.toString());\n } else if (n < 10000) {\n return chalk.yellow(n.toString());\n }\n return chalk.red(n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return chalk.yellow(row);\n }\n return chalk.red(row);\n }\n return chalk.green(row);\n}\n"],"names":["v.string","v.boolean","v"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-ast.d.ts","sourceRoot":"","sources":["../../../../analyze-query/src/run-ast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAOjD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,kCAAkC,CAAC;AAErE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,iDAAiD,CAAC;AACxF,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,gCAAgC,CAAC;AAExD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAG3E,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,+CAA+C,CAAC;AACrF,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG;IAC1B,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,oBAAoB,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9C,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,WAAW,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC5C,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF,wBAAsB,MAAM,CAC1B,EAAE,EAAE,UAAU,EACd,YAAY,EAAE,YAAY,EAC1B,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,OAAO,EACtB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAiG7B"}
1
+ {"version":3,"file":"run-ast.d.ts","sourceRoot":"","sources":["../../../../analyze-query/src/run-ast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAQjD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,kCAAkC,CAAC;AAErE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,iDAAiD,CAAC;AACxF,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,gCAAgC,CAAC;AAExD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0CAA0C,CAAC;AAG3E,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,+CAA+C,CAAC;AACrF,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,wBAAwB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG;IAC1B,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,oBAAoB,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9C,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,WAAW,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC5C,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF,wBAAsB,MAAM,CAC1B,EAAE,EAAE,UAAU,EACd,YAAY,EAAE,YAAY,EAC1B,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,OAAO,EACtB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAyG7B"}
@@ -3,12 +3,13 @@ import { formatOutput } from "../../ast-to-zql/src/format.js";
3
3
  import { assert } from "../../shared/src/asserts.js";
4
4
  import { must } from "../../shared/src/must.js";
5
5
  import { transformAndHashQuery } from "../../zero-cache/src/auth/read-authorizer.js";
6
+ import { computeZqlSpecs } from "../../zero-cache/src/db/lite-tables.js";
6
7
  import { hydrate } from "../../zero-cache/src/services/view-syncer/pipeline-driver.js";
7
8
  import { mapAST } from "../../zero-protocol/src/ast.js";
8
9
  import { hashOfAST } from "../../zero-protocol/src/query-hash.js";
9
10
  import { buildPipeline } from "../../zql/src/builder/builder.js";
10
11
  async function runAst(lc, clientSchema, ast, isTransformed, options) {
11
- const { clientToServerMapper, permissions, host } = options;
12
+ const { clientToServerMapper, permissions, host, db } = options;
12
13
  const result = {
13
14
  warnings: [],
14
15
  syncedRows: void 0,
@@ -52,7 +53,15 @@ async function runAst(lc, clientSchema, ast, isTransformed, options) {
52
53
  let syncedRowCount = 0;
53
54
  const rowsByTable = {};
54
55
  const seenByTable = /* @__PURE__ */ new Set();
55
- for (const rowChange of hydrate(pipeline, hashOfAST(ast), clientSchema)) {
56
+ const tableSpecs = computeZqlSpecs(lc, db, {
57
+ includeBackfillingColumns: false
58
+ });
59
+ for (const rowChange of hydrate(
60
+ pipeline,
61
+ hashOfAST(ast),
62
+ clientSchema,
63
+ tableSpecs
64
+ )) {
56
65
  if (rowChange === "yield") {
57
66
  continue;
58
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"run-ast.js","sources":["../../../../analyze-query/src/run-ast.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {must} from '../../shared/src/must.ts';\nimport type {JWTAuth} from '../../zero-cache/src/auth/auth.ts';\nimport {transformAndHashQuery} from '../../zero-cache/src/auth/read-authorizer.ts';\nimport type {LiteAndZqlSpec} from '../../zero-cache/src/db/specs.ts';\nimport {hydrate} from '../../zero-cache/src/services/view-syncer/pipeline-driver.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../zero-protocol/src/ast.ts';\nimport {mapAST} from '../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../zero-protocol/src/client-schema.ts';\nimport type {Row} from '../../zero-protocol/src/data.ts';\nimport {hashOfAST} from '../../zero-protocol/src/query-hash.ts';\nimport type {PermissionsConfig} from '../../zero-schema/src/compiled-permissions.ts';\nimport type {NameMapper} from '../../zero-schema/src/name-mapper.ts';\nimport {\n buildPipeline,\n type BuilderDelegate,\n} from '../../zql/src/builder/builder.ts';\nimport type {Database} from '../../zqlite/src/db.ts';\n\nexport type RunAstOptions = {\n applyPermissions?: boolean | undefined;\n authData?: string | undefined;\n clientToServerMapper?: NameMapper | undefined;\n db: Database;\n host: BuilderDelegate;\n permissions?: PermissionsConfig | undefined;\n syncedRows?: boolean | undefined;\n tableSpecs: Map<string, LiteAndZqlSpec>;\n vendedRows?: boolean | undefined;\n};\n\nexport async function runAst(\n lc: LogContext,\n clientSchema: ClientSchema,\n ast: AST,\n isTransformed: boolean,\n options: RunAstOptions,\n): Promise<AnalyzeQueryResult> {\n const {clientToServerMapper, permissions, host} = options;\n const result: AnalyzeQueryResult = {\n warnings: [],\n syncedRows: undefined,\n syncedRowCount: 0,\n start: 0,\n end: 0,\n afterPermissions: undefined,\n readRows: undefined,\n readRowCountsByQuery: {},\n readRowCount: undefined,\n };\n\n if (!isTransformed) {\n // map the AST to server names if not already transformed\n ast = mapAST(ast, must(clientToServerMapper));\n }\n if (options.applyPermissions) {\n result.warnings.push(\n 'Permissions are deprecated and will be removed in an upcoming release. See: https://zero.rocicorp.dev/docs/auth.',\n );\n\n const authData = options.authData ? JSON.parse(options.authData) : {};\n if (!options.authData) {\n result.warnings.push(\n 'No auth data provided. Permission rules will compare to `NULL` wherever an auth data field is referenced.',\n );\n }\n const auth: JWTAuth = {type: 'jwt', raw: '', decoded: authData};\n ast = transformAndHashQuery(\n lc,\n 'clientGroupIDForAnalyze',\n ast,\n must(\n permissions,\n 'Permissions are required when applyPermissions is true',\n ),\n auth,\n false,\n ).transformedAst;\n result.afterPermissions = await formatOutput(ast.table + astToZQL(ast));\n }\n const pipeline = buildPipeline(ast, host, 'query-id');\n\n const start = performance.now();\n\n let syncedRowCount = 0;\n const rowsByTable: Record<string, Row[]> = {};\n const seenByTable: Set<string> = new Set();\n for (const rowChange of hydrate(pipeline, hashOfAST(ast), clientSchema)) {\n if (rowChange === 'yield') {\n continue;\n }\n assert(\n rowChange.type === 'add',\n () => `Expected rowChange type 'add', got '${rowChange.type}'`,\n );\n\n let rows: Row[] = rowsByTable[rowChange.table];\n const s = rowChange.table + '.' + JSON.stringify(rowChange.row);\n if (seenByTable.has(s)) {\n continue; // skip duplicates\n }\n syncedRowCount++;\n seenByTable.add(s);\n if (options.syncedRows) {\n if (!rows) {\n rows = [];\n rowsByTable[rowChange.table] = rows;\n }\n rows.push(rowChange.row);\n }\n }\n\n const end = performance.now();\n if (options.syncedRows) {\n result.syncedRows = rowsByTable;\n }\n result.start = start;\n result.end = end;\n\n // Always include the count of synced and vended rows.\n result.syncedRowCount = syncedRowCount;\n result.readRowCountsByQuery = host.debug?.getVendedRowCounts() ?? {};\n let readRowCount = 0;\n for (const c of Object.values(result.readRowCountsByQuery)) {\n for (const v of Object.values(c)) {\n readRowCount += v;\n }\n }\n result.readRowCount = readRowCount;\n\n if (options.vendedRows) {\n result.readRows = host.debug?.getVendedRows();\n }\n return result;\n}\n"],"names":[],"mappings":";;;;;;;;;AAmCA,eAAsB,OACpB,IACA,cACA,KACA,eACA,SAC6B;AAC7B,QAAM,EAAC,sBAAsB,aAAa,KAAA,IAAQ;AAClD,QAAM,SAA6B;AAAA,IACjC,UAAU,CAAA;AAAA,IACV,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,KAAK;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,sBAAsB,CAAA;AAAA,IACtB,cAAc;AAAA,EAAA;AAGhB,MAAI,CAAC,eAAe;AAElB,UAAM,OAAO,KAAK,KAAK,oBAAoB,CAAC;AAAA,EAC9C;AACA,MAAI,QAAQ,kBAAkB;AAC5B,WAAO,SAAS;AAAA,MACd;AAAA,IAAA;AAGF,UAAM,WAAW,QAAQ,WAAW,KAAK,MAAM,QAAQ,QAAQ,IAAI,CAAA;AACnE,QAAI,CAAC,QAAQ,UAAU;AACrB,aAAO,SAAS;AAAA,QACd;AAAA,MAAA;AAAA,IAEJ;AACA,UAAM,OAAgB,EAAuB,SAAS,SAAA;AACtD,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,MAAA;AAAA,MAEF;AAAA,MACA;AAAA,IAAA,EACA;AACF,WAAO,mBAAmB,MAAM,aAAa,IAAI,QAAQ,SAAS,GAAG,CAAC;AAAA,EACxE;AACA,QAAM,WAAW,cAAc,KAAK,MAAM,UAAU;AAEpD,QAAM,QAAQ,YAAY,IAAA;AAE1B,MAAI,iBAAiB;AACrB,QAAM,cAAqC,CAAA;AAC3C,QAAM,kCAA+B,IAAA;AACrC,aAAW,aAAa,QAAQ,UAAU,UAAU,GAAG,GAAG,YAAY,GAAG;AACvE,QAAI,cAAc,SAAS;AACzB;AAAA,IACF;AACA;AAAA,MACE,UAAU,SAAS;AAAA,MACnB,MAAM,uCAAuC,UAAU,IAAI;AAAA,IAAA;AAG7D,QAAI,OAAc,YAAY,UAAU,KAAK;AAC7C,UAAM,IAAI,UAAU,QAAQ,MAAM,KAAK,UAAU,UAAU,GAAG;AAC9D,QAAI,YAAY,IAAI,CAAC,GAAG;AACtB;AAAA,IACF;AACA;AACA,gBAAY,IAAI,CAAC;AACjB,QAAI,QAAQ,YAAY;AACtB,UAAI,CAAC,MAAM;AACT,eAAO,CAAA;AACP,oBAAY,UAAU,KAAK,IAAI;AAAA,MACjC;AACA,WAAK,KAAK,UAAU,GAAG;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,MAAM,YAAY,IAAA;AACxB,MAAI,QAAQ,YAAY;AACtB,WAAO,aAAa;AAAA,EACtB;AACA,SAAO,QAAQ;AACf,SAAO,MAAM;AAGb,SAAO,iBAAiB;AACxB,SAAO,uBAAuB,KAAK,OAAO,mBAAA,KAAwB,CAAA;AAClE,MAAI,eAAe;AACnB,aAAW,KAAK,OAAO,OAAO,OAAO,oBAAoB,GAAG;AAC1D,eAAW,KAAK,OAAO,OAAO,CAAC,GAAG;AAChC,sBAAgB;AAAA,IAClB;AAAA,EACF;AACA,SAAO,eAAe;AAEtB,MAAI,QAAQ,YAAY;AACtB,WAAO,WAAW,KAAK,OAAO,cAAA;AAAA,EAChC;AACA,SAAO;AACT;"}
1
+ {"version":3,"file":"run-ast.js","sources":["../../../../analyze-query/src/run-ast.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {must} from '../../shared/src/must.ts';\nimport type {JWTAuth} from '../../zero-cache/src/auth/auth.ts';\nimport {transformAndHashQuery} from '../../zero-cache/src/auth/read-authorizer.ts';\nimport {computeZqlSpecs} from '../../zero-cache/src/db/lite-tables.ts';\nimport type {LiteAndZqlSpec} from '../../zero-cache/src/db/specs.ts';\nimport {hydrate} from '../../zero-cache/src/services/view-syncer/pipeline-driver.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../zero-protocol/src/ast.ts';\nimport {mapAST} from '../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../zero-protocol/src/client-schema.ts';\nimport type {Row} from '../../zero-protocol/src/data.ts';\nimport {hashOfAST} from '../../zero-protocol/src/query-hash.ts';\nimport type {PermissionsConfig} from '../../zero-schema/src/compiled-permissions.ts';\nimport type {NameMapper} from '../../zero-schema/src/name-mapper.ts';\nimport {\n buildPipeline,\n type BuilderDelegate,\n} from '../../zql/src/builder/builder.ts';\nimport type {Database} from '../../zqlite/src/db.ts';\n\nexport type RunAstOptions = {\n applyPermissions?: boolean | undefined;\n authData?: string | undefined;\n clientToServerMapper?: NameMapper | undefined;\n db: Database;\n host: BuilderDelegate;\n permissions?: PermissionsConfig | undefined;\n syncedRows?: boolean | undefined;\n tableSpecs: Map<string, LiteAndZqlSpec>;\n vendedRows?: boolean | undefined;\n};\n\nexport async function runAst(\n lc: LogContext,\n clientSchema: ClientSchema,\n ast: AST,\n isTransformed: boolean,\n options: RunAstOptions,\n): Promise<AnalyzeQueryResult> {\n const {clientToServerMapper, permissions, host, db} = options;\n const result: AnalyzeQueryResult = {\n warnings: [],\n syncedRows: undefined,\n syncedRowCount: 0,\n start: 0,\n end: 0,\n afterPermissions: undefined,\n readRows: undefined,\n readRowCountsByQuery: {},\n readRowCount: undefined,\n };\n\n if (!isTransformed) {\n // map the AST to server names if not already transformed\n ast = mapAST(ast, must(clientToServerMapper));\n }\n if (options.applyPermissions) {\n result.warnings.push(\n 'Permissions are deprecated and will be removed in an upcoming release. See: https://zero.rocicorp.dev/docs/auth.',\n );\n\n const authData = options.authData ? JSON.parse(options.authData) : {};\n if (!options.authData) {\n result.warnings.push(\n 'No auth data provided. Permission rules will compare to `NULL` wherever an auth data field is referenced.',\n );\n }\n const auth: JWTAuth = {type: 'jwt', raw: '', decoded: authData};\n ast = transformAndHashQuery(\n lc,\n 'clientGroupIDForAnalyze',\n ast,\n must(\n permissions,\n 'Permissions are required when applyPermissions is true',\n ),\n auth,\n false,\n ).transformedAst;\n result.afterPermissions = await formatOutput(ast.table + astToZQL(ast));\n }\n const pipeline = buildPipeline(ast, host, 'query-id');\n\n const start = performance.now();\n\n let syncedRowCount = 0;\n const rowsByTable: Record<string, Row[]> = {};\n const seenByTable: Set<string> = new Set();\n const tableSpecs = computeZqlSpecs(lc, db, {\n includeBackfillingColumns: false,\n });\n for (const rowChange of hydrate(\n pipeline,\n hashOfAST(ast),\n clientSchema,\n tableSpecs,\n )) {\n if (rowChange === 'yield') {\n continue;\n }\n assert(\n rowChange.type === 'add',\n () => `Expected rowChange type 'add', got '${rowChange.type}'`,\n );\n\n let rows: Row[] = rowsByTable[rowChange.table];\n const s = rowChange.table + '.' + JSON.stringify(rowChange.row);\n if (seenByTable.has(s)) {\n continue; // skip duplicates\n }\n syncedRowCount++;\n seenByTable.add(s);\n if (options.syncedRows) {\n if (!rows) {\n rows = [];\n rowsByTable[rowChange.table] = rows;\n }\n rows.push(rowChange.row);\n }\n }\n\n const end = performance.now();\n if (options.syncedRows) {\n result.syncedRows = rowsByTable;\n }\n result.start = start;\n result.end = end;\n\n // Always include the count of synced and vended rows.\n result.syncedRowCount = syncedRowCount;\n result.readRowCountsByQuery = host.debug?.getVendedRowCounts() ?? {};\n let readRowCount = 0;\n for (const c of Object.values(result.readRowCountsByQuery)) {\n for (const v of Object.values(c)) {\n readRowCount += v;\n }\n }\n result.readRowCount = readRowCount;\n\n if (options.vendedRows) {\n result.readRows = host.debug?.getVendedRows();\n }\n return result;\n}\n"],"names":[],"mappings":";;;;;;;;;;AAoCA,eAAsB,OACpB,IACA,cACA,KACA,eACA,SAC6B;AAC7B,QAAM,EAAC,sBAAsB,aAAa,MAAM,OAAM;AACtD,QAAM,SAA6B;AAAA,IACjC,UAAU,CAAA;AAAA,IACV,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,KAAK;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,sBAAsB,CAAA;AAAA,IACtB,cAAc;AAAA,EAAA;AAGhB,MAAI,CAAC,eAAe;AAElB,UAAM,OAAO,KAAK,KAAK,oBAAoB,CAAC;AAAA,EAC9C;AACA,MAAI,QAAQ,kBAAkB;AAC5B,WAAO,SAAS;AAAA,MACd;AAAA,IAAA;AAGF,UAAM,WAAW,QAAQ,WAAW,KAAK,MAAM,QAAQ,QAAQ,IAAI,CAAA;AACnE,QAAI,CAAC,QAAQ,UAAU;AACrB,aAAO,SAAS;AAAA,QACd;AAAA,MAAA;AAAA,IAEJ;AACA,UAAM,OAAgB,EAAuB,SAAS,SAAA;AACtD,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,MAAA;AAAA,MAEF;AAAA,MACA;AAAA,IAAA,EACA;AACF,WAAO,mBAAmB,MAAM,aAAa,IAAI,QAAQ,SAAS,GAAG,CAAC;AAAA,EACxE;AACA,QAAM,WAAW,cAAc,KAAK,MAAM,UAAU;AAEpD,QAAM,QAAQ,YAAY,IAAA;AAE1B,MAAI,iBAAiB;AACrB,QAAM,cAAqC,CAAA;AAC3C,QAAM,kCAA+B,IAAA;AACrC,QAAM,aAAa,gBAAgB,IAAI,IAAI;AAAA,IACzC,2BAA2B;AAAA,EAAA,CAC5B;AACD,aAAW,aAAa;AAAA,IACtB;AAAA,IACA,UAAU,GAAG;AAAA,IACb;AAAA,IACA;AAAA,EAAA,GACC;AACD,QAAI,cAAc,SAAS;AACzB;AAAA,IACF;AACA;AAAA,MACE,UAAU,SAAS;AAAA,MACnB,MAAM,uCAAuC,UAAU,IAAI;AAAA,IAAA;AAG7D,QAAI,OAAc,YAAY,UAAU,KAAK;AAC7C,UAAM,IAAI,UAAU,QAAQ,MAAM,KAAK,UAAU,UAAU,GAAG;AAC9D,QAAI,YAAY,IAAI,CAAC,GAAG;AACtB;AAAA,IACF;AACA;AACA,gBAAY,IAAI,CAAC;AACjB,QAAI,QAAQ,YAAY;AACtB,UAAI,CAAC,MAAM;AACT,eAAO,CAAA;AACP,oBAAY,UAAU,KAAK,IAAI;AAAA,MACjC;AACA,WAAK,KAAK,UAAU,GAAG;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,MAAM,YAAY,IAAA;AACxB,MAAI,QAAQ,YAAY;AACtB,WAAO,aAAa;AAAA,EACtB;AACA,SAAO,QAAQ;AACf,SAAO,MAAM;AAGb,SAAO,iBAAiB;AACxB,SAAO,uBAAuB,KAAK,OAAO,mBAAA,KAAwB,CAAA;AAClE,MAAI,eAAe;AACnB,aAAW,KAAK,OAAO,OAAO,OAAO,oBAAoB,GAAG;AAC1D,eAAW,KAAK,OAAO,OAAO,CAAC,GAAG;AAChC,sBAAgB;AAAA,IAClB;AAAA,EACF;AACA,SAAO,eAAe;AAEtB,MAAI,QAAQ,YAAY;AACtB,WAAO,WAAW,KAAK,OAAO,cAAA;AAAA,EAChC;AACA,SAAO;AACT;"}
@@ -1,4 +1,4 @@
1
- const version = "0.26.1-canary.11";
1
+ const version = "0.26.1-canary.12";
2
2
  const packageJson = {
3
3
  version
4
4
  };
@@ -3,8 +3,9 @@ import type { Database } from '../../../zqlite/src/db.ts';
3
3
  import type { LiteAndZqlSpec, LiteIndexSpec, LiteTableSpec } from './specs.ts';
4
4
  export type LiteTableSpecWithReplicationStatus = LiteTableSpec & {
5
5
  readonly backfilling?: string[];
6
+ readonly minRowVersion?: string | null;
6
7
  };
7
- export declare function listTables(db: Database, useColumnMetadata?: boolean): LiteTableSpecWithReplicationStatus[];
8
+ export declare function listTables(db: Database, useColumnMetadata?: boolean, useTableMetadata?: boolean): LiteTableSpecWithReplicationStatus[];
8
9
  export declare function listIndexes(db: Database): LiteIndexSpec[];
9
10
  export type ZqlSpecOptions = {
10
11
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"lite-tables.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/lite-tables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,2BAA2B,CAAC;AAaxD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EAGd,MAAM,YAAY,CAAC;AAWpB,MAAM,MAAM,kCAAkC,GAAG,aAAa,GAAG;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAMF,wBAAgB,UAAU,CACxB,EAAE,EAAE,QAAQ,EACZ,iBAAiB,UAAO,GACvB,kCAAkC,EAAE,CAoFtC;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,aAAa,EAAE,CAyCzD;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;OAMG;IACH,yBAAyB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,cAAc,EACpB,UAAU,GAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa,EACnD,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACtC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAS7B;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,kCAAkC,EAAE,EAC5C,OAAO,EAAE,aAAa,EAAE,EACxB,EAAC,yBAAyB,EAAC,EAAE,cAAc,EAC3C,UAAU,GAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa,EACnD,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EACvC,EAAE,CAAC,EAAE,UAAU,GACd,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CA6E7B;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EACvC,SAAS,EAAE,MAAM,GAChB,cAAc,CAWhB"}
1
+ {"version":3,"file":"lite-tables.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/lite-tables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,2BAA2B,CAAC;AAcxD,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EAGd,MAAM,YAAY,CAAC;AAWpB,MAAM,MAAM,kCAAkC,GAAG,aAAa,GAAG;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC,CAAC;AAOF,wBAAgB,UAAU,CACxB,EAAE,EAAE,QAAQ,EACZ,iBAAiB,UAAO,EACxB,gBAAgB,UAAO,GACtB,kCAAkC,EAAE,CAwFtC;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,aAAa,EAAE,CAyCzD;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;OAMG;IACH,yBAAyB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,cAAc,EACpB,UAAU,GAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa,EACnD,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACtC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAS7B;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,kCAAkC,EAAE,EAC5C,OAAO,EAAE,aAAa,EAAE,EACxB,EAAC,yBAAyB,EAAC,EAAE,cAAc,EAC3C,UAAU,GAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa,EACnD,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EACvC,EAAE,CAAC,EAAE,UAAU,GACd,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CA8E7B;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EACvC,SAAS,EAAE,MAAM,GAChB,cAAc,CAWhB"}
@@ -3,9 +3,10 @@ import { difference } from "../../../shared/src/set-utils.js";
3
3
  import { parse } from "../../../shared/src/valita.js";
4
4
  import { primaryKeySchema } from "../../../zero-protocol/src/primary-key.js";
5
5
  import { ColumnMetadataStore, metadataToLiteTypeString } from "../services/replicator/schema/column-metadata.js";
6
+ import { TableMetadataTracker } from "../services/replicator/schema/table-metadata.js";
6
7
  import { liteTypeToZqlValueType, nullableUpstream, mapLiteDataTypeToZqlSchemaValue, isArray, isEnum } from "../types/lite.js";
7
8
  import { Enum, Base } from "./postgres-type-class-enum.js";
8
- function listTables(db, useColumnMetadata = true) {
9
+ function listTables(db, useColumnMetadata = true, useTableMetadata = true) {
9
10
  const columns = db.prepare(
10
11
  `
11
12
  SELECT
@@ -23,6 +24,7 @@ function listTables(db, useColumnMetadata = true) {
23
24
  AND m.name NOT LIKE '_litestream_%'
24
25
  `
25
26
  ).all();
27
+ const minRowVersions = useTableMetadata ? new TableMetadataTracker(db).getMinRowVersions() : /* @__PURE__ */ new Map();
26
28
  const tables = [];
27
29
  let table;
28
30
  columns.forEach((col) => {
@@ -30,7 +32,8 @@ function listTables(db, useColumnMetadata = true) {
30
32
  table = {
31
33
  name: col.table,
32
34
  columns: {},
33
- backfilling: []
35
+ backfilling: [],
36
+ minRowVersion: minRowVersions.get(col.table) ?? null
34
37
  };
35
38
  tables.push(table);
36
39
  }
@@ -147,7 +150,8 @@ function computeZqlSpecsFromLiteSpecs(tables, indexes, { includeBackfillingColum
147
150
  uniqueKeys: uniqueKeys.map((key) => parse(key.sort(), primaryKeySchema)),
148
151
  allPotentialPrimaryKeys: keys.map(
149
152
  (key) => parse(key.sort(), primaryKeySchema)
150
- )
153
+ ),
154
+ minRowVersion: fullTable.minRowVersion ?? null
151
155
  };
152
156
  tableSpecs.set(tableSpec.name, {
153
157
  tableSpec,
@@ -1 +1 @@
1
- {"version":3,"file":"lite-tables.js","sources":["../../../../../zero-cache/src/db/lite-tables.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {difference} from '../../../shared/src/set-utils.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {primaryKeySchema} from '../../../zero-protocol/src/primary-key.ts';\nimport type {Database} from '../../../zqlite/src/db.ts';\nimport {\n ColumnMetadataStore,\n metadataToLiteTypeString,\n} from '../services/replicator/schema/column-metadata.ts';\nimport {\n isArray,\n isEnum,\n liteTypeToZqlValueType,\n mapLiteDataTypeToZqlSchemaValue,\n nullableUpstream,\n} from '../types/lite.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport type {\n LiteAndZqlSpec,\n LiteIndexSpec,\n LiteTableSpec,\n MutableLiteIndexSpec,\n MutableLiteTableSpec,\n} from './specs.ts';\n\ntype ColumnInfo = {\n table: string;\n name: string;\n type: string;\n notNull: number;\n dflt: string | null;\n keyPos: number;\n};\n\nexport type LiteTableSpecWithReplicationStatus = LiteTableSpec & {\n readonly backfilling?: string[];\n};\n\ntype MutableLiteTableSpecWithReplicationStatus = MutableLiteTableSpec & {\n backfilling: string[];\n};\n\nexport function listTables(\n db: Database,\n useColumnMetadata = true,\n): LiteTableSpecWithReplicationStatus[] {\n const columns = db\n .prepare(\n `\n SELECT\n m.name as \"table\",\n p.name as name,\n p.type as type,\n p.\"notnull\" as \"notNull\",\n p.dflt_value as \"dflt\",\n p.pk as keyPos\n FROM sqlite_master as m\n LEFT JOIN pragma_table_info(m.name) as p\n WHERE m.type = 'table'\n AND m.name NOT LIKE 'sqlite_%'\n AND m.name NOT LIKE '_zero.%'\n AND m.name NOT LIKE '_litestream_%'\n `,\n )\n .all() as ColumnInfo[];\n\n const tables: LiteTableSpec[] = [];\n let table: MutableLiteTableSpecWithReplicationStatus | undefined;\n\n columns.forEach(col => {\n if (col.table !== table?.name) {\n // New table\n table = {\n name: col.table,\n columns: {},\n backfilling: [],\n };\n tables.push(table);\n }\n\n // Try to read from metadata table first, fall back to SQLite column type\n let dataType: string;\n let elemPgTypeClass:\n | typeof PostgresTypeClass.Base\n | typeof PostgresTypeClass.Enum\n | null;\n\n const metadata = useColumnMetadata\n ? ColumnMetadataStore.getInstance(db)?.getColumn(col.table, col.name)\n : undefined;\n if (metadata) {\n // Read from metadata table and convert to pipe notation\n dataType = metadataToLiteTypeString(metadata);\n elemPgTypeClass = metadata.isArray\n ? metadata.isEnum\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n } else {\n // Fall back to reading from SQLite column type (pipe notation)\n dataType = col.type;\n elemPgTypeClass = isArray(col.type)\n ? isEnum(col.type)\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n }\n\n table.columns[col.name] = {\n pos: Object.keys(table.columns).length + 1,\n dataType,\n characterMaximumLength: null,\n notNull: col.notNull !== 0,\n dflt: col.dflt,\n elemPgTypeClass,\n };\n if (metadata?.isBackfilling) {\n table.backfilling.push(col.name);\n }\n if (col.keyPos) {\n table.primaryKey ??= [];\n while (table.primaryKey.length < col.keyPos) {\n table.primaryKey.push('');\n }\n table.primaryKey[col.keyPos - 1] = col.name;\n }\n });\n\n return tables;\n}\n\nexport function listIndexes(db: Database): LiteIndexSpec[] {\n const indexes = db\n .prepare(\n `SELECT \n idx.name as indexName, \n idx.tbl_name as tableName, \n info.\"unique\" as \"unique\",\n col.name as column,\n CASE WHEN col.desc = 0 THEN 'ASC' ELSE 'DESC' END as dir\n FROM sqlite_master as idx\n JOIN pragma_index_list(idx.tbl_name) AS info ON info.name = idx.name\n JOIN pragma_index_xinfo(idx.name) as col\n WHERE idx.type = 'index' AND \n col.key = 1 AND\n idx.tbl_name NOT LIKE '_zero.%'\n ORDER BY idx.name, col.seqno ASC`,\n )\n .all() as {\n indexName: string;\n tableName: string;\n unique: number;\n column: string;\n dir: 'ASC' | 'DESC';\n }[];\n\n const ret: MutableLiteIndexSpec[] = [];\n for (const {indexName: name, tableName, unique, column, dir} of indexes) {\n if (ret.at(-1)?.name === name) {\n // Aggregate multiple column names into the array.\n must(ret.at(-1)).columns[column] = dir;\n } else {\n ret.push({\n tableName,\n name,\n columns: {[column]: dir},\n unique: unique !== 0,\n });\n }\n }\n\n return ret;\n}\n\nexport type ZqlSpecOptions = {\n /**\n * Controls whether to include backfilling columns in the computed\n * LiteAndZqlSpec. In general, backfilling columns should be include\n * in \"replication\" logic (copying data from upstream to the replica),\n * and excluded from \"sync\" logic (sending data from the replica to\n * clients).\n */\n includeBackfillingColumns: boolean;\n};\n\n/**\n * Computes a TableSpec \"view\" of the replicated data that is\n * suitable for processing / consumption for the client. This\n * includes:\n * * excluding tables without a PRIMARY KEY or UNIQUE INDEX\n * * excluding columns with types that are not supported by ZQL\n * * choosing columns to use as the primary key amongst those\n * in unique indexes\n *\n * @param tableSpecs an optional map to reset and populate\n * @param fullTables an optional map to receive the full table specs,\n * which may include tables and columns that are not synced to\n * the client because they lack a primary key or are of unsupported\n * data types.\n */\nexport function computeZqlSpecs(\n lc: LogContext,\n replica: Database,\n opts: ZqlSpecOptions,\n tableSpecs: Map<string, LiteAndZqlSpec> = new Map(),\n fullTables?: Map<string, LiteTableSpec>,\n): Map<string, LiteAndZqlSpec> {\n return computeZqlSpecsFromLiteSpecs(\n listTables(replica),\n listIndexes(replica),\n opts,\n tableSpecs,\n fullTables,\n lc,\n );\n}\n\nexport function computeZqlSpecsFromLiteSpecs(\n tables: LiteTableSpecWithReplicationStatus[],\n indexes: LiteIndexSpec[],\n {includeBackfillingColumns}: ZqlSpecOptions,\n tableSpecs: Map<string, LiteAndZqlSpec> = new Map(),\n fullTables?: Map<string, LiteTableSpec>,\n lc?: LogContext,\n): Map<string, LiteAndZqlSpec> {\n tableSpecs.clear();\n fullTables?.clear();\n\n const uniqueIndexColumns = new Map<string, string[][]>();\n for (const {tableName, columns} of indexes.filter(idx => idx.unique)) {\n if (!uniqueIndexColumns.has(tableName)) {\n uniqueIndexColumns.set(tableName, []);\n }\n uniqueIndexColumns.get(tableName)?.push(Object.keys(columns));\n }\n\n tables.forEach(fullTable => {\n fullTables?.set(fullTable.name, fullTable);\n\n const backfilling = new Set(fullTable.backfilling);\n // Only include columns that:\n // - have a defined ZQL Value\n // - aren't backfilling if `includeBackfillingColumns` is false\n const visibleColumns = Object.entries(fullTable.columns).filter(\n ([col, {dataType}]) =>\n liteTypeToZqlValueType(dataType) &&\n (includeBackfillingColumns || !backfilling.has(col)),\n );\n const notNullColumns = new Set(\n visibleColumns\n .filter(\n ([col, {dataType}]) =>\n !nullableUpstream(dataType) || fullTable.primaryKey?.includes(col),\n )\n .map(([col]) => col),\n );\n\n const uniqueKeys = uniqueIndexColumns.get(fullTable.name) ?? [];\n // Examine all column combinations that can serve as a primary key,\n // i.e. excluding indexes over nullable or unsynced columns.\n const keys = uniqueKeys.filter(\n key => difference(new Set(key), notNullColumns).size === 0,\n );\n if (keys.length === 0) {\n // Only include tables with a row key.\n //\n // Note that this will automatically exclude tables that are being\n // backfilled (when includeBackfillingColumns is `false`) since candidate\n // keys only include visible columns.\n lc?.debug?.(\n `not syncing table ${fullTable.name} because it ` +\n (backfilling.size ? 'is being backfilled' : 'has no primary key'),\n );\n return;\n }\n // Pick the \"best\" (i.e. shortest) key for default IVM operations.\n const primaryKey = keys.sort(keyCmp)[0];\n\n const tableSpec = {\n ...fullTable,\n columns: Object.fromEntries(visibleColumns),\n // normalize (sort) keys to minimize creating new objects.\n // See row-key.ts: normalizedKeyOrder()\n primaryKey: v.parse(primaryKey.sort(), primaryKeySchema),\n uniqueKeys: uniqueKeys.map(key => v.parse(key.sort(), primaryKeySchema)),\n allPotentialPrimaryKeys: keys.map(key =>\n v.parse(key.sort(), primaryKeySchema),\n ),\n };\n\n tableSpecs.set(tableSpec.name, {\n tableSpec,\n zqlSpec: Object.fromEntries(\n Object.entries(tableSpec.columns).map(([name, {dataType}]) => [\n name,\n mapLiteDataTypeToZqlSchemaValue(dataType),\n ]),\n ),\n });\n });\n return tableSpecs;\n}\n\nexport function mustGetTableSpec(\n tableSpecs: Map<string, LiteAndZqlSpec>,\n tableName: string,\n): LiteAndZqlSpec {\n const tableSpec = tableSpecs.get(tableName);\n if (!tableSpec) {\n throw new Error(\n `table '${tableName}' is not one of: ${[...tableSpecs.keys()]\n .filter(t => !t.includes('.') && !t.startsWith('_litestream_'))\n .sort()}. ` +\n `Check the spelling and ensure that the table has a primary key.`,\n );\n }\n return tableSpec;\n}\n\n// Deterministic comparator for favoring shorter row keys.\nfunction keyCmp(a: string[], b: string[]) {\n if (a.length !== b.length) {\n return a.length - b.length; // Fewer columns are better.\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] < b[i]) {\n return -1;\n }\n if (a[i] > b[i]) {\n return 1;\n }\n }\n return 0;\n}\n"],"names":["PostgresTypeClass.Enum","PostgresTypeClass.Base","v.parse"],"mappings":"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"}
1
+ {"version":3,"file":"lite-tables.js","sources":["../../../../../zero-cache/src/db/lite-tables.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {must} from '../../../shared/src/must.ts';\nimport {difference} from '../../../shared/src/set-utils.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {primaryKeySchema} from '../../../zero-protocol/src/primary-key.ts';\nimport type {Database} from '../../../zqlite/src/db.ts';\nimport {\n ColumnMetadataStore,\n metadataToLiteTypeString,\n} from '../services/replicator/schema/column-metadata.ts';\nimport {TableMetadataTracker} from '../services/replicator/schema/table-metadata.ts';\nimport {\n isArray,\n isEnum,\n liteTypeToZqlValueType,\n mapLiteDataTypeToZqlSchemaValue,\n nullableUpstream,\n} from '../types/lite.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport type {\n LiteAndZqlSpec,\n LiteIndexSpec,\n LiteTableSpec,\n MutableLiteIndexSpec,\n MutableLiteTableSpec,\n} from './specs.ts';\n\ntype ColumnInfo = {\n table: string;\n name: string;\n type: string;\n notNull: number;\n dflt: string | null;\n keyPos: number;\n};\n\nexport type LiteTableSpecWithReplicationStatus = LiteTableSpec & {\n readonly backfilling?: string[];\n readonly minRowVersion?: string | null;\n};\n\ntype MutableLiteTableSpecWithReplicationStatus = MutableLiteTableSpec & {\n backfilling: string[];\n minRowVersion: string | null;\n};\n\nexport function listTables(\n db: Database,\n useColumnMetadata = true,\n useTableMetadata = true,\n): LiteTableSpecWithReplicationStatus[] {\n const columns = db\n .prepare(\n `\n SELECT\n m.name as \"table\",\n p.name as name,\n p.type as type,\n p.\"notnull\" as \"notNull\",\n p.dflt_value as \"dflt\",\n p.pk as keyPos\n FROM sqlite_master as m\n LEFT JOIN pragma_table_info(m.name) as p\n WHERE m.type = 'table'\n AND m.name NOT LIKE 'sqlite_%'\n AND m.name NOT LIKE '_zero.%'\n AND m.name NOT LIKE '_litestream_%'\n `,\n )\n .all() as ColumnInfo[];\n\n const minRowVersions = useTableMetadata\n ? new TableMetadataTracker(db).getMinRowVersions()\n : new Map();\n const tables: LiteTableSpecWithReplicationStatus[] = [];\n let table: MutableLiteTableSpecWithReplicationStatus | undefined;\n\n columns.forEach(col => {\n if (col.table !== table?.name) {\n // New table\n table = {\n name: col.table,\n columns: {},\n backfilling: [],\n minRowVersion: minRowVersions.get(col.table) ?? null,\n };\n tables.push(table);\n }\n\n // Try to read from metadata table first, fall back to SQLite column type\n let dataType: string;\n let elemPgTypeClass:\n | typeof PostgresTypeClass.Base\n | typeof PostgresTypeClass.Enum\n | null;\n\n const metadata = useColumnMetadata\n ? ColumnMetadataStore.getInstance(db)?.getColumn(col.table, col.name)\n : undefined;\n if (metadata) {\n // Read from metadata table and convert to pipe notation\n dataType = metadataToLiteTypeString(metadata);\n elemPgTypeClass = metadata.isArray\n ? metadata.isEnum\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n } else {\n // Fall back to reading from SQLite column type (pipe notation)\n dataType = col.type;\n elemPgTypeClass = isArray(col.type)\n ? isEnum(col.type)\n ? PostgresTypeClass.Enum\n : PostgresTypeClass.Base\n : null;\n }\n\n table.columns[col.name] = {\n pos: Object.keys(table.columns).length + 1,\n dataType,\n characterMaximumLength: null,\n notNull: col.notNull !== 0,\n dflt: col.dflt,\n elemPgTypeClass,\n };\n if (metadata?.isBackfilling) {\n table.backfilling.push(col.name);\n }\n if (col.keyPos) {\n table.primaryKey ??= [];\n while (table.primaryKey.length < col.keyPos) {\n table.primaryKey.push('');\n }\n table.primaryKey[col.keyPos - 1] = col.name;\n }\n });\n\n return tables;\n}\n\nexport function listIndexes(db: Database): LiteIndexSpec[] {\n const indexes = db\n .prepare(\n `SELECT \n idx.name as indexName, \n idx.tbl_name as tableName, \n info.\"unique\" as \"unique\",\n col.name as column,\n CASE WHEN col.desc = 0 THEN 'ASC' ELSE 'DESC' END as dir\n FROM sqlite_master as idx\n JOIN pragma_index_list(idx.tbl_name) AS info ON info.name = idx.name\n JOIN pragma_index_xinfo(idx.name) as col\n WHERE idx.type = 'index' AND \n col.key = 1 AND\n idx.tbl_name NOT LIKE '_zero.%'\n ORDER BY idx.name, col.seqno ASC`,\n )\n .all() as {\n indexName: string;\n tableName: string;\n unique: number;\n column: string;\n dir: 'ASC' | 'DESC';\n }[];\n\n const ret: MutableLiteIndexSpec[] = [];\n for (const {indexName: name, tableName, unique, column, dir} of indexes) {\n if (ret.at(-1)?.name === name) {\n // Aggregate multiple column names into the array.\n must(ret.at(-1)).columns[column] = dir;\n } else {\n ret.push({\n tableName,\n name,\n columns: {[column]: dir},\n unique: unique !== 0,\n });\n }\n }\n\n return ret;\n}\n\nexport type ZqlSpecOptions = {\n /**\n * Controls whether to include backfilling columns in the computed\n * LiteAndZqlSpec. In general, backfilling columns should be include\n * in \"replication\" logic (copying data from upstream to the replica),\n * and excluded from \"sync\" logic (sending data from the replica to\n * clients).\n */\n includeBackfillingColumns: boolean;\n};\n\n/**\n * Computes a TableSpec \"view\" of the replicated data that is\n * suitable for processing / consumption for the client. This\n * includes:\n * * excluding tables without a PRIMARY KEY or UNIQUE INDEX\n * * excluding columns with types that are not supported by ZQL\n * * choosing columns to use as the primary key amongst those\n * in unique indexes\n *\n * @param tableSpecs an optional map to reset and populate\n * @param fullTables an optional map to receive the full table specs,\n * which may include tables and columns that are not synced to\n * the client because they lack a primary key or are of unsupported\n * data types.\n */\nexport function computeZqlSpecs(\n lc: LogContext,\n replica: Database,\n opts: ZqlSpecOptions,\n tableSpecs: Map<string, LiteAndZqlSpec> = new Map(),\n fullTables?: Map<string, LiteTableSpec>,\n): Map<string, LiteAndZqlSpec> {\n return computeZqlSpecsFromLiteSpecs(\n listTables(replica),\n listIndexes(replica),\n opts,\n tableSpecs,\n fullTables,\n lc,\n );\n}\n\nexport function computeZqlSpecsFromLiteSpecs(\n tables: LiteTableSpecWithReplicationStatus[],\n indexes: LiteIndexSpec[],\n {includeBackfillingColumns}: ZqlSpecOptions,\n tableSpecs: Map<string, LiteAndZqlSpec> = new Map(),\n fullTables?: Map<string, LiteTableSpec>,\n lc?: LogContext,\n): Map<string, LiteAndZqlSpec> {\n tableSpecs.clear();\n fullTables?.clear();\n\n const uniqueIndexColumns = new Map<string, string[][]>();\n for (const {tableName, columns} of indexes.filter(idx => idx.unique)) {\n if (!uniqueIndexColumns.has(tableName)) {\n uniqueIndexColumns.set(tableName, []);\n }\n uniqueIndexColumns.get(tableName)?.push(Object.keys(columns));\n }\n\n tables.forEach(fullTable => {\n fullTables?.set(fullTable.name, fullTable);\n\n const backfilling = new Set(fullTable.backfilling);\n // Only include columns that:\n // - have a defined ZQL Value\n // - aren't backfilling if `includeBackfillingColumns` is false\n const visibleColumns = Object.entries(fullTable.columns).filter(\n ([col, {dataType}]) =>\n liteTypeToZqlValueType(dataType) &&\n (includeBackfillingColumns || !backfilling.has(col)),\n );\n const notNullColumns = new Set(\n visibleColumns\n .filter(\n ([col, {dataType}]) =>\n !nullableUpstream(dataType) || fullTable.primaryKey?.includes(col),\n )\n .map(([col]) => col),\n );\n\n const uniqueKeys = uniqueIndexColumns.get(fullTable.name) ?? [];\n // Examine all column combinations that can serve as a primary key,\n // i.e. excluding indexes over nullable or unsynced columns.\n const keys = uniqueKeys.filter(\n key => difference(new Set(key), notNullColumns).size === 0,\n );\n if (keys.length === 0) {\n // Only include tables with a row key.\n //\n // Note that this will automatically exclude tables that are being\n // backfilled (when includeBackfillingColumns is `false`) since candidate\n // keys only include visible columns.\n lc?.debug?.(\n `not syncing table ${fullTable.name} because it ` +\n (backfilling.size ? 'is being backfilled' : 'has no primary key'),\n );\n return;\n }\n // Pick the \"best\" (i.e. shortest) key for default IVM operations.\n const primaryKey = keys.sort(keyCmp)[0];\n\n const tableSpec = {\n ...fullTable,\n columns: Object.fromEntries(visibleColumns),\n // normalize (sort) keys to minimize creating new objects.\n // See row-key.ts: normalizedKeyOrder()\n primaryKey: v.parse(primaryKey.sort(), primaryKeySchema),\n uniqueKeys: uniqueKeys.map(key => v.parse(key.sort(), primaryKeySchema)),\n allPotentialPrimaryKeys: keys.map(key =>\n v.parse(key.sort(), primaryKeySchema),\n ),\n minRowVersion: fullTable.minRowVersion ?? null,\n };\n\n tableSpecs.set(tableSpec.name, {\n tableSpec,\n zqlSpec: Object.fromEntries(\n Object.entries(tableSpec.columns).map(([name, {dataType}]) => [\n name,\n mapLiteDataTypeToZqlSchemaValue(dataType),\n ]),\n ),\n });\n });\n return tableSpecs;\n}\n\nexport function mustGetTableSpec(\n tableSpecs: Map<string, LiteAndZqlSpec>,\n tableName: string,\n): LiteAndZqlSpec {\n const tableSpec = tableSpecs.get(tableName);\n if (!tableSpec) {\n throw new Error(\n `table '${tableName}' is not one of: ${[...tableSpecs.keys()]\n .filter(t => !t.includes('.') && !t.startsWith('_litestream_'))\n .sort()}. ` +\n `Check the spelling and ensure that the table has a primary key.`,\n );\n }\n return tableSpec;\n}\n\n// Deterministic comparator for favoring shorter row keys.\nfunction keyCmp(a: string[], b: string[]) {\n if (a.length !== b.length) {\n return a.length - b.length; // Fewer columns are better.\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] < b[i]) {\n return -1;\n }\n if (a[i] > b[i]) {\n return 1;\n }\n }\n return 0;\n}\n"],"names":["PostgresTypeClass.Enum","PostgresTypeClass.Base","v.parse"],"mappings":"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"}
@@ -75,7 +75,7 @@ export declare const publishedTableSpec: v.ObjectType<Omit<Omit<{
75
75
  }, undefined>;
76
76
  export type MutableLiteTableSpec = v.Infer<typeof liteTableSpec>;
77
77
  export type LiteTableSpec = Readonly<MutableLiteTableSpec>;
78
- export type LiteTableSpecWithKeys = Omit<LiteTableSpec, 'primaryKey'> & {
78
+ export type LiteTableSpecWithKeysAndVersion = Omit<LiteTableSpec, 'primaryKey'> & {
79
79
  /**
80
80
  * All keys associated with a unique index. Includes indexes with
81
81
  * nullable columns.
@@ -92,9 +92,15 @@ export type LiteTableSpecWithKeys = Omit<LiteTableSpec, 'primaryKey'> & {
92
92
  * columns, i.e. suitable as a primary key.
93
93
  */
94
94
  allPotentialPrimaryKeys: PrimaryKey[];
95
+ /**
96
+ * The minimum `_0_version` value for every row in the table. If this is
97
+ * present, `_0_version` value in the row itself should only be used if
98
+ * it is greater (i.e. later) than the `minRowVersion`.
99
+ */
100
+ minRowVersion: string | null;
95
101
  };
96
102
  export type LiteAndZqlSpec = {
97
- tableSpec: LiteTableSpecWithKeys;
103
+ tableSpec: LiteTableSpecWithKeysAndVersion;
98
104
  zqlSpec: Record<string, SchemaValue>;
99
105
  };
100
106
  export type TableSpec = Readonly<v.Infer<typeof tableSpec>>;
@@ -1 +1 @@
1
- {"version":3,"file":"specs.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/specs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAC1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0CAA0C,CAAC;AAI1E,eAAO,MAAM,iBAAiB,iDAQ7B,CAAC;AASF,eAAO,MAAM,UAAU;;;;;;;;aAarB,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC;AAM9D,eAAO,MAAM,aAAa;;;;;;;;;;;;aAIxB,CAAC;AAEH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;aAEpB,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQ7B,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,aAAa,EAAE,YAAY,CAAC,GAAG;IACtE;;;OAGG;IACH,UAAU,EAAE,UAAU,EAAE,CAAC;IAEzB;;;;OAIG;IACH,UAAU,EAAE,UAAU,CAAC;IAEvB;;;OAGG;IACH,uBAAuB,EAAE,UAAU,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,qBAAqB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC;AAE9E,eAAO,MAAM,eAAe,wBAAgC,CAAC;AAE7D,eAAO,MAAM,aAAa;;;;;aAKxB,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAC;AAE3D,eAAO,MAAM,SAAS;;;;;;;aAEpB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAEhE,eAAO,MAAM,kBAAkB;;;;;;;;;;;aAI7B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,YAAY,CAC3C,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CACnC,CAAC"}
1
+ {"version":3,"file":"specs.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/specs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,CAAC,MAAM,+BAA+B,CAAC;AACnD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,2CAA2C,CAAC;AAC1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0CAA0C,CAAC;AAI1E,eAAO,MAAM,iBAAiB,iDAQ7B,CAAC;AASF,eAAO,MAAM,UAAU;;;;;;;;aAarB,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC;AAM9D,eAAO,MAAM,aAAa;;;;;;;;;;;;aAIxB,CAAC;AAEH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;aAEpB,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQ7B,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAC;AAE3D,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,aAAa,EACb,YAAY,CACb,GAAG;IACF;;;OAGG;IACH,UAAU,EAAE,UAAU,EAAE,CAAC;IAEzB;;;;OAIG;IACH,UAAU,EAAE,UAAU,CAAC;IAEvB;;;OAGG;IACH,uBAAuB,EAAE,UAAU,EAAE,CAAC;IAEtC;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,+BAA+B,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC;AAE9E,eAAO,MAAM,eAAe,wBAAgC,CAAC;AAE7D,eAAO,MAAM,aAAa;;;;;aAKxB,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAC;AAE3D,eAAO,MAAM,SAAS;;;;;;;aAEpB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC;AAEhE,eAAO,MAAM,kBAAkB;;;;;;;;;;;aAI7B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,YAAY,CAC3C,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CACnC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"specs.js","sources":["../../../../../zero-cache/src/db/specs.ts"],"sourcesContent":["import type {DeepReadonly} from '../../../shared/src/json.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-schema/src/table-schema.ts';\nimport * as PostgresReplicaIdentity from './postgres-replica-identity-enum.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\n\nexport const pgTypeClassSchema = v.literalUnion(\n PostgresTypeClass.Base,\n PostgresTypeClass.Composite,\n PostgresTypeClass.Domain,\n PostgresTypeClass.Enum,\n PostgresTypeClass.Pseudo,\n PostgresTypeClass.Range,\n PostgresTypeClass.Multirange,\n);\n\nconst pgReplicaIdentitySchema = v.literalUnion(\n PostgresReplicaIdentity.Default,\n PostgresReplicaIdentity.Nothing,\n PostgresReplicaIdentity.Full,\n PostgresReplicaIdentity.Index,\n);\n\nexport const columnSpec = v.object({\n pos: v.number(),\n dataType: v.string(),\n pgTypeClass: pgTypeClassSchema.optional(),\n\n // If the column is an array, this will be the type of the\n // elements in the array. If the column is not an array,\n // this will be null.\n elemPgTypeClass: pgTypeClassSchema.nullable().optional(),\n\n characterMaximumLength: v.number().nullable().optional(),\n notNull: v.boolean().nullable().optional(),\n dflt: v.string().nullable().optional(),\n});\n\nexport type ColumnSpec = Readonly<v.Infer<typeof columnSpec>>;\n\nconst publishedColumnSpec = columnSpec.extend({\n typeOID: v.number(),\n});\n\nexport const liteTableSpec = v.object({\n name: v.string(),\n columns: v.record(columnSpec),\n primaryKey: v.array(v.string()).optional(),\n});\n\nexport const tableSpec = liteTableSpec.extend({\n schema: v.string(),\n});\n\nexport const publishedTableSpec = tableSpec.extend({\n oid: v.number(),\n // Always present for new instances (e.g. from DDL triggers), but\n // may from `initialSchema` object stored in the `replicas` table.\n schemaOID: v.number().optional(),\n columns: v.record(publishedColumnSpec),\n replicaIdentity: pgReplicaIdentitySchema.optional(),\n publications: v.record(v.object({rowFilter: v.string().nullable()})),\n});\n\nexport type MutableLiteTableSpec = v.Infer<typeof liteTableSpec>;\n\nexport type LiteTableSpec = Readonly<MutableLiteTableSpec>;\n\nexport type LiteTableSpecWithKeys = Omit<LiteTableSpec, 'primaryKey'> & {\n /**\n * All keys associated with a unique index. Includes indexes with\n * nullable columns.\n */\n uniqueKeys: PrimaryKey[];\n\n /**\n * The key selected to act as the \"primary key\". Primary keys\n * are not explicitly set on the replica, but an appropriate\n * unique index is required.\n */\n primaryKey: PrimaryKey; // note: required\n\n /**\n * All keys associated with a unique index over non-null\n * columns, i.e. suitable as a primary key.\n */\n allPotentialPrimaryKeys: PrimaryKey[];\n};\n\nexport type LiteAndZqlSpec = {\n tableSpec: LiteTableSpecWithKeys;\n zqlSpec: Record<string, SchemaValue>;\n};\n\nexport type TableSpec = Readonly<v.Infer<typeof tableSpec>>;\n\nexport type PublishedTableSpec = Readonly<v.Infer<typeof publishedTableSpec>>;\n\nexport const directionSchema = v.literalUnion('ASC', 'DESC');\n\nexport const liteIndexSpec = v.object({\n name: v.string(),\n tableName: v.string(),\n unique: v.boolean(),\n columns: v.record(directionSchema),\n});\n\nexport type MutableLiteIndexSpec = v.Infer<typeof liteIndexSpec>;\n\nexport type LiteIndexSpec = Readonly<MutableLiteIndexSpec>;\n\nexport const indexSpec = liteIndexSpec.extend({\n schema: v.string(),\n});\n\nexport type IndexSpec = DeepReadonly<v.Infer<typeof indexSpec>>;\n\nexport const publishedIndexSpec = indexSpec.extend({\n isReplicaIdentity: v.boolean().optional(),\n isPrimaryKey: v.boolean().optional(),\n isImmediate: v.boolean().optional(),\n});\n\nexport type PublishedIndexSpec = DeepReadonly<\n v.Infer<typeof publishedIndexSpec>\n>;\n"],"names":["v.literalUnion","PostgresTypeClass.Base","PostgresTypeClass.Composite","PostgresTypeClass.Domain","PostgresTypeClass.Enum","PostgresTypeClass.Pseudo","PostgresTypeClass.Range","PostgresTypeClass.Multirange","PostgresReplicaIdentity.Default","PostgresReplicaIdentity.Nothing","PostgresReplicaIdentity.Full","PostgresReplicaIdentity.Index","v.object","v.number","v.string","v.boolean","v.record","v.array"],"mappings":";;;;AAOO,MAAM,oBAAoBA;AAAAA,EAC/BC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AACF;AAEA,MAAM,0BAA0BP;AAAAA,EAC9BQ;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AACF;AAEO,MAAM,aAAaC,OAAS;AAAA,EACjC,KAAKC,OAAE;AAAA,EACP,UAAUC,OAAE;AAAA,EACZ,aAAa,kBAAkB,SAAA;AAAA;AAAA;AAAA;AAAA,EAK/B,iBAAiB,kBAAkB,SAAA,EAAW,SAAA;AAAA,EAE9C,wBAAwBD,OAAE,EAAS,SAAA,EAAW,SAAA;AAAA,EAC9C,SAASE,QAAE,EAAU,SAAA,EAAW,SAAA;AAAA,EAChC,MAAMD,OAAE,EAAS,SAAA,EAAW,SAAA;AAC9B,CAAC;AAID,MAAM,sBAAsB,WAAW,OAAO;AAAA,EAC5C,SAASD,OAAE;AACb,CAAC;AAEM,MAAM,gBAAgBD,OAAS;AAAA,EACpC,MAAME,OAAE;AAAA,EACR,SAASE,OAAS,UAAU;AAAA,EAC5B,YAAYC,MAAQH,OAAE,CAAQ,EAAE,SAAA;AAClC,CAAC;AAEM,MAAM,YAAY,cAAc,OAAO;AAAA,EAC5C,QAAQA,OAAE;AACZ,CAAC;AAEM,MAAM,qBAAqB,UAAU,OAAO;AAAA,EACjD,KAAKD,OAAE;AAAA;AAAA;AAAA,EAGP,WAAWA,OAAE,EAAS,SAAA;AAAA,EACtB,SAASG,OAAS,mBAAmB;AAAA,EACrC,iBAAiB,wBAAwB,SAAA;AAAA,EACzC,cAAcA,OAASJ,OAAS,EAAC,WAAWE,OAAE,EAAS,SAAA,GAAW,CAAC;AACrE,CAAC;AAoCM,MAAM,kBAAkBd,aAAe,OAAO,MAAM;AAEpD,MAAM,gBAAgBY,OAAS;AAAA,EACpC,MAAME,OAAE;AAAA,EACR,WAAWA,OAAE;AAAA,EACb,QAAQC,QAAE;AAAA,EACV,SAASC,OAAS,eAAe;AACnC,CAAC;AAMM,MAAM,YAAY,cAAc,OAAO;AAAA,EAC5C,QAAQF,OAAE;AACZ,CAAC;AAIM,MAAM,qBAAqB,UAAU,OAAO;AAAA,EACjD,mBAAmBC,QAAE,EAAU,SAAA;AAAA,EAC/B,cAAcA,QAAE,EAAU,SAAA;AAAA,EAC1B,aAAaA,QAAE,EAAU,SAAA;AAC3B,CAAC;"}
1
+ {"version":3,"file":"specs.js","sources":["../../../../../zero-cache/src/db/specs.ts"],"sourcesContent":["import type {DeepReadonly} from '../../../shared/src/json.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-schema/src/table-schema.ts';\nimport * as PostgresReplicaIdentity from './postgres-replica-identity-enum.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\n\nexport const pgTypeClassSchema = v.literalUnion(\n PostgresTypeClass.Base,\n PostgresTypeClass.Composite,\n PostgresTypeClass.Domain,\n PostgresTypeClass.Enum,\n PostgresTypeClass.Pseudo,\n PostgresTypeClass.Range,\n PostgresTypeClass.Multirange,\n);\n\nconst pgReplicaIdentitySchema = v.literalUnion(\n PostgresReplicaIdentity.Default,\n PostgresReplicaIdentity.Nothing,\n PostgresReplicaIdentity.Full,\n PostgresReplicaIdentity.Index,\n);\n\nexport const columnSpec = v.object({\n pos: v.number(),\n dataType: v.string(),\n pgTypeClass: pgTypeClassSchema.optional(),\n\n // If the column is an array, this will be the type of the\n // elements in the array. If the column is not an array,\n // this will be null.\n elemPgTypeClass: pgTypeClassSchema.nullable().optional(),\n\n characterMaximumLength: v.number().nullable().optional(),\n notNull: v.boolean().nullable().optional(),\n dflt: v.string().nullable().optional(),\n});\n\nexport type ColumnSpec = Readonly<v.Infer<typeof columnSpec>>;\n\nconst publishedColumnSpec = columnSpec.extend({\n typeOID: v.number(),\n});\n\nexport const liteTableSpec = v.object({\n name: v.string(),\n columns: v.record(columnSpec),\n primaryKey: v.array(v.string()).optional(),\n});\n\nexport const tableSpec = liteTableSpec.extend({\n schema: v.string(),\n});\n\nexport const publishedTableSpec = tableSpec.extend({\n oid: v.number(),\n // Always present for new instances (e.g. from DDL triggers), but\n // may from `initialSchema` object stored in the `replicas` table.\n schemaOID: v.number().optional(),\n columns: v.record(publishedColumnSpec),\n replicaIdentity: pgReplicaIdentitySchema.optional(),\n publications: v.record(v.object({rowFilter: v.string().nullable()})),\n});\n\nexport type MutableLiteTableSpec = v.Infer<typeof liteTableSpec>;\n\nexport type LiteTableSpec = Readonly<MutableLiteTableSpec>;\n\nexport type LiteTableSpecWithKeysAndVersion = Omit<\n LiteTableSpec,\n 'primaryKey'\n> & {\n /**\n * All keys associated with a unique index. Includes indexes with\n * nullable columns.\n */\n uniqueKeys: PrimaryKey[];\n\n /**\n * The key selected to act as the \"primary key\". Primary keys\n * are not explicitly set on the replica, but an appropriate\n * unique index is required.\n */\n primaryKey: PrimaryKey; // note: required\n\n /**\n * All keys associated with a unique index over non-null\n * columns, i.e. suitable as a primary key.\n */\n allPotentialPrimaryKeys: PrimaryKey[];\n\n /**\n * The minimum `_0_version` value for every row in the table. If this is\n * present, `_0_version` value in the row itself should only be used if\n * it is greater (i.e. later) than the `minRowVersion`.\n */\n minRowVersion: string | null;\n};\n\nexport type LiteAndZqlSpec = {\n tableSpec: LiteTableSpecWithKeysAndVersion;\n zqlSpec: Record<string, SchemaValue>;\n};\n\nexport type TableSpec = Readonly<v.Infer<typeof tableSpec>>;\n\nexport type PublishedTableSpec = Readonly<v.Infer<typeof publishedTableSpec>>;\n\nexport const directionSchema = v.literalUnion('ASC', 'DESC');\n\nexport const liteIndexSpec = v.object({\n name: v.string(),\n tableName: v.string(),\n unique: v.boolean(),\n columns: v.record(directionSchema),\n});\n\nexport type MutableLiteIndexSpec = v.Infer<typeof liteIndexSpec>;\n\nexport type LiteIndexSpec = Readonly<MutableLiteIndexSpec>;\n\nexport const indexSpec = liteIndexSpec.extend({\n schema: v.string(),\n});\n\nexport type IndexSpec = DeepReadonly<v.Infer<typeof indexSpec>>;\n\nexport const publishedIndexSpec = indexSpec.extend({\n isReplicaIdentity: v.boolean().optional(),\n isPrimaryKey: v.boolean().optional(),\n isImmediate: v.boolean().optional(),\n});\n\nexport type PublishedIndexSpec = DeepReadonly<\n v.Infer<typeof publishedIndexSpec>\n>;\n"],"names":["v.literalUnion","PostgresTypeClass.Base","PostgresTypeClass.Composite","PostgresTypeClass.Domain","PostgresTypeClass.Enum","PostgresTypeClass.Pseudo","PostgresTypeClass.Range","PostgresTypeClass.Multirange","PostgresReplicaIdentity.Default","PostgresReplicaIdentity.Nothing","PostgresReplicaIdentity.Full","PostgresReplicaIdentity.Index","v.object","v.number","v.string","v.boolean","v.record","v.array"],"mappings":";;;;AAOO,MAAM,oBAAoBA;AAAAA,EAC/BC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AACF;AAEA,MAAM,0BAA0BP;AAAAA,EAC9BQ;AAAAA,EACAC;AAAAA,EACAC;AAAAA,EACAC;AACF;AAEO,MAAM,aAAaC,OAAS;AAAA,EACjC,KAAKC,OAAE;AAAA,EACP,UAAUC,OAAE;AAAA,EACZ,aAAa,kBAAkB,SAAA;AAAA;AAAA;AAAA;AAAA,EAK/B,iBAAiB,kBAAkB,SAAA,EAAW,SAAA;AAAA,EAE9C,wBAAwBD,OAAE,EAAS,SAAA,EAAW,SAAA;AAAA,EAC9C,SAASE,QAAE,EAAU,SAAA,EAAW,SAAA;AAAA,EAChC,MAAMD,OAAE,EAAS,SAAA,EAAW,SAAA;AAC9B,CAAC;AAID,MAAM,sBAAsB,WAAW,OAAO;AAAA,EAC5C,SAASD,OAAE;AACb,CAAC;AAEM,MAAM,gBAAgBD,OAAS;AAAA,EACpC,MAAME,OAAE;AAAA,EACR,SAASE,OAAS,UAAU;AAAA,EAC5B,YAAYC,MAAQH,OAAE,CAAQ,EAAE,SAAA;AAClC,CAAC;AAEM,MAAM,YAAY,cAAc,OAAO;AAAA,EAC5C,QAAQA,OAAE;AACZ,CAAC;AAEM,MAAM,qBAAqB,UAAU,OAAO;AAAA,EACjD,KAAKD,OAAE;AAAA;AAAA;AAAA,EAGP,WAAWA,OAAE,EAAS,SAAA;AAAA,EACtB,SAASG,OAAS,mBAAmB;AAAA,EACrC,iBAAiB,wBAAwB,SAAA;AAAA,EACzC,cAAcA,OAASJ,OAAS,EAAC,WAAWE,OAAE,EAAS,SAAA,GAAW,CAAC;AACrE,CAAC;AA8CM,MAAM,kBAAkBd,aAAe,OAAO,MAAM;AAEpD,MAAM,gBAAgBY,OAAS;AAAA,EACpC,MAAME,OAAE;AAAA,EACR,WAAWA,OAAE;AAAA,EACb,QAAQC,QAAE;AAAA,EACV,SAASC,OAAS,eAAe;AACnC,CAAC;AAMM,MAAM,YAAY,cAAc,OAAO;AAAA,EAC5C,QAAQF,OAAE;AACZ,CAAC;AAIM,MAAM,qBAAqB,UAAU,OAAO;AAAA,EACjD,mBAAmBC,QAAE,EAAU,SAAA;AAAA,EAC/B,cAAcA,QAAE,EAAU,SAAA;AAAA,EAC1B,aAAaA,QAAE,EAAU,SAAA;AAC3B,CAAC;"}
@@ -83,6 +83,7 @@ async function analyzeQuery(lc, config, clientSchema, ast, syncedRows = true, ve
83
83
  syncedRows,
84
84
  vendedRows,
85
85
  auth,
86
+ db,
86
87
  tableSpecs,
87
88
  permissions,
88
89
  costModel,
@@ -1 +1 @@
1
- {"version":3,"file":"analyze.js","sources":["../../../../../zero-cache/src/services/analyze.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {AnalyzeQueryResult} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../zero-protocol/src/client-schema.ts';\nimport type {PermissionsConfig} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {Debug} from '../../../zql/src/builder/debug-delegate.ts';\nimport {MemoryStorage} from '../../../zql/src/ivm/memory-storage.ts';\nimport {\n AccumulatorDebugger,\n serializePlanDebugEvents,\n} from '../../../zql/src/planner/planner-debug.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {explainQueries} from '../../../zqlite/src/explain-queries.ts';\nimport {createSQLiteCostModel} from '../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../zqlite/src/table-source.ts';\nimport type {JWTAuth} from '../auth/auth.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../db/specs.ts';\nimport {runAst} from './run-ast.ts';\nimport {TimeSliceTimer} from './view-syncer/view-syncer.ts';\n\nconst TIME_SLICE_LAP_THRESHOLD_MS = 200;\n\nexport async function analyzeQuery(\n lc: LogContext,\n config: NormalizedZeroConfig,\n clientSchema: ClientSchema,\n ast: AST,\n syncedRows = true,\n vendedRows = false,\n permissions?: PermissionsConfig,\n auth?: JWTAuth,\n joinPlans = false,\n): Promise<AnalyzeQueryResult> {\n using db = new Database(lc, config.replica.file);\n const fullTables = new Map<string, LiteTableSpec>();\n const tableSpecs = new Map<string, LiteAndZqlSpec>();\n const tables = new Map<string, TableSource>();\n\n computeZqlSpecs(\n lc,\n db,\n {includeBackfillingColumns: false},\n tableSpecs,\n fullTables,\n );\n\n const planDebugger = joinPlans ? new AccumulatorDebugger() : undefined;\n const costModel = joinPlans\n ? createSQLiteCostModel(db, tableSpecs)\n : undefined;\n const timer = await new TimeSliceTimer(lc).start();\n const shouldYield = () => timer.elapsedLap() > TIME_SLICE_LAP_THRESHOLD_MS;\n const yieldProcess = () => timer.yieldProcess();\n const result = await runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: permissions !== undefined,\n syncedRows,\n vendedRows,\n auth,\n db,\n tableSpecs,\n permissions,\n costModel,\n planDebugger,\n host: {\n debug: new Debug(),\n getSource(tableName: string) {\n let source = tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(tableSpecs, tableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n shouldYield,\n );\n tables.set(tableName, source);\n return source;\n },\n createStorage() {\n return new MemoryStorage();\n },\n decorateSourceInput: input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n },\n yieldProcess,\n );\n\n result.sqlitePlans = explainQueries(result.readRowCountsByQuery ?? {}, db);\n\n if (planDebugger) {\n result.joinPlans = serializePlanDebugEvents(planDebugger.events);\n }\n\n return result;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,8BAA8B;AAEpC,eAAsB,aACpB,IACA,QACA,cACA,KACA,aAAa,MACb,aAAa,OACb,aACA,MACA,YAAY,OACiB;AAC7B;AAAA;AAAA,UAAM,KAAK,oBAAI,SAAS,IAAI,OAAO,QAAQ,IAAI;AAC/C,UAAM,iCAAiB,IAAA;AACvB,UAAM,iCAAiB,IAAA;AACvB,UAAM,6BAAa,IAAA;AAEnB;AAAA,MACE;AAAA,MACA;AAAA,MACA,EAAC,2BAA2B,MAAA;AAAA,MAC5B;AAAA,MACA;AAAA,IAAA;AAGF,UAAM,eAAe,YAAY,IAAI,oBAAA,IAAwB;AAC7D,UAAM,YAAY,YACd,sBAAsB,IAAI,UAAU,IACpC;AACJ,UAAM,QAAQ,MAAM,IAAI,eAAe,EAAE,EAAE,MAAA;AAC3C,UAAM,cAAc,MAAM,MAAM,WAAA,IAAe;AAC/C,UAAM,eAAe,MAAM,MAAM,aAAA;AACjC,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,kBAAkB,gBAAgB;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,UACJ,OAAO,IAAI,MAAA;AAAA,UACX,UAAU,WAAmB;AAC3B,gBAAI,SAAS,OAAO,IAAI,SAAS;AACjC,gBAAI,QAAQ;AACV,qBAAO;AAAA,YACT;AAEA,kBAAM,YAAY,iBAAiB,YAAY,SAAS;AACxD,kBAAM,EAAC,eAAc,UAAU;AAE/B,qBAAS,IAAI;AAAA,cACX;AAAA,cACA,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,UAAU;AAAA,cACV;AAAA,cACA;AAAA,YAAA;AAEF,mBAAO,IAAI,WAAW,MAAM;AAC5B,mBAAO;AAAA,UACT;AAAA,UACA,gBAAgB;AACd,mBAAO,IAAI,cAAA;AAAA,UACb;AAAA,UACA,qBAAqB,CAAA,UAAS;AAAA,UAC9B,eAAe,CAAA,UAAS;AAAA,UACxB,UAAU;AAAA,UAAC;AAAA,UACX,qBAAqB,CAAA,UAAS;AAAA,QAAA;AAAA,MAChC;AAAA,MAEF;AAAA,IAAA;AAGF,WAAO,cAAc,eAAe,OAAO,wBAAwB,CAAA,GAAI,EAAE;AAEzE,QAAI,cAAc;AAChB,aAAO,YAAY,yBAAyB,aAAa,MAAM;AAAA,IACjE;AAEA,WAAO;AAAA,WA5EP;AAAA;AAAA;AAAA;AAAA;AA6EF;"}
1
+ {"version":3,"file":"analyze.js","sources":["../../../../../zero-cache/src/services/analyze.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {AnalyzeQueryResult} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {ClientSchema} from '../../../zero-protocol/src/client-schema.ts';\nimport type {PermissionsConfig} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {Debug} from '../../../zql/src/builder/debug-delegate.ts';\nimport {MemoryStorage} from '../../../zql/src/ivm/memory-storage.ts';\nimport {\n AccumulatorDebugger,\n serializePlanDebugEvents,\n} from '../../../zql/src/planner/planner-debug.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {explainQueries} from '../../../zqlite/src/explain-queries.ts';\nimport {createSQLiteCostModel} from '../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../zqlite/src/table-source.ts';\nimport type {JWTAuth} from '../auth/auth.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../db/specs.ts';\nimport {runAst} from './run-ast.ts';\nimport {TimeSliceTimer} from './view-syncer/view-syncer.ts';\n\nconst TIME_SLICE_LAP_THRESHOLD_MS = 200;\n\nexport async function analyzeQuery(\n lc: LogContext,\n config: NormalizedZeroConfig,\n clientSchema: ClientSchema,\n ast: AST,\n syncedRows = true,\n vendedRows = false,\n permissions?: PermissionsConfig,\n auth?: JWTAuth,\n joinPlans = false,\n): Promise<AnalyzeQueryResult> {\n using db = new Database(lc, config.replica.file);\n const fullTables = new Map<string, LiteTableSpec>();\n const tableSpecs = new Map<string, LiteAndZqlSpec>();\n const tables = new Map<string, TableSource>();\n\n computeZqlSpecs(\n lc,\n db,\n {includeBackfillingColumns: false},\n tableSpecs,\n fullTables,\n );\n\n const planDebugger = joinPlans ? new AccumulatorDebugger() : undefined;\n const costModel = joinPlans\n ? createSQLiteCostModel(db, tableSpecs)\n : undefined;\n const timer = await new TimeSliceTimer(lc).start();\n const shouldYield = () => timer.elapsedLap() > TIME_SLICE_LAP_THRESHOLD_MS;\n const yieldProcess = () => timer.yieldProcess();\n const result = await runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: permissions !== undefined,\n syncedRows,\n vendedRows,\n auth,\n db,\n tableSpecs,\n permissions,\n costModel,\n planDebugger,\n host: {\n debug: new Debug(),\n getSource(tableName: string) {\n let source = tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(tableSpecs, tableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n shouldYield,\n );\n tables.set(tableName, source);\n return source;\n },\n createStorage() {\n return new MemoryStorage();\n },\n decorateSourceInput: input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n },\n yieldProcess,\n );\n\n result.sqlitePlans = explainQueries(result.readRowCountsByQuery ?? {}, db);\n\n if (planDebugger) {\n result.joinPlans = serializePlanDebugEvents(planDebugger.events);\n }\n\n return result;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAM,8BAA8B;AAEpC,eAAsB,aACpB,IACA,QACA,cACA,KACA,aAAa,MACb,aAAa,OACb,aACA,MACA,YAAY,OACiB;AAC7B;AAAA;AAAA,UAAM,KAAK,oBAAI,SAAS,IAAI,OAAO,QAAQ,IAAI;AAC/C,UAAM,iCAAiB,IAAA;AACvB,UAAM,iCAAiB,IAAA;AACvB,UAAM,6BAAa,IAAA;AAEnB;AAAA,MACE;AAAA,MACA;AAAA,MACA,EAAC,2BAA2B,MAAA;AAAA,MAC5B;AAAA,MACA;AAAA,IAAA;AAGF,UAAM,eAAe,YAAY,IAAI,oBAAA,IAAwB;AAC7D,UAAM,YAAY,YACd,sBAAsB,IAAI,UAAU,IACpC;AACJ,UAAM,QAAQ,MAAM,IAAI,eAAe,EAAE,EAAE,MAAA;AAC3C,UAAM,cAAc,MAAM,MAAM,WAAA,IAAe;AAC/C,UAAM,eAAe,MAAM,MAAM,aAAA;AACjC,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,kBAAkB,gBAAgB;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,UACJ,OAAO,IAAI,MAAA;AAAA,UACX,UAAU,WAAmB;AAC3B,gBAAI,SAAS,OAAO,IAAI,SAAS;AACjC,gBAAI,QAAQ;AACV,qBAAO;AAAA,YACT;AAEA,kBAAM,YAAY,iBAAiB,YAAY,SAAS;AACxD,kBAAM,EAAC,eAAc,UAAU;AAE/B,qBAAS,IAAI;AAAA,cACX;AAAA,cACA,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,UAAU;AAAA,cACV;AAAA,cACA;AAAA,YAAA;AAEF,mBAAO,IAAI,WAAW,MAAM;AAC5B,mBAAO;AAAA,UACT;AAAA,UACA,gBAAgB;AACd,mBAAO,IAAI,cAAA;AAAA,UACb;AAAA,UACA,qBAAqB,CAAA,UAAS;AAAA,UAC9B,eAAe,CAAA,UAAS;AAAA,UACxB,UAAU;AAAA,UAAC;AAAA,UACX,qBAAqB,CAAA,UAAS;AAAA,QAAA;AAAA,MAChC;AAAA,MAEF;AAAA,IAAA;AAGF,WAAO,cAAc,eAAe,OAAO,wBAAwB,CAAA,GAAI,EAAE;AAEzE,QAAI,cAAc;AAChB,aAAO,YAAY,yBAAyB,aAAa,MAAM;AAAA,IACjE;AAEA,WAAO;AAAA,WA5EP;AAAA;AAAA;AAAA;AAAA;AA6EF;"}
@@ -5,5 +5,6 @@ export declare function initReplica(log: LogContext, debugName: string, dbPath:
5
5
  export declare function upgradeReplica(log: LogContext, debugName: string, dbPath: string): Promise<void>;
6
6
  export declare const CREATE_V6_COLUMN_METADATA_TABLE = "\n CREATE TABLE \"_zero.column_metadata\" (\n table_name TEXT NOT NULL,\n column_name TEXT NOT NULL,\n upstream_type TEXT NOT NULL,\n is_not_null INTEGER NOT NULL,\n is_enum INTEGER NOT NULL,\n is_array INTEGER NOT NULL,\n character_max_length INTEGER,\n PRIMARY KEY (table_name, column_name)\n );\n";
7
7
  export declare const CREATE_V7_CHANGE_LOG = "\n CREATE TABLE \"_zero.changeLog2\" (\n \"stateVersion\" TEXT NOT NULL,\n \"pos\" INT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"rowKey\" TEXT NOT NULL,\n \"op\" TEXT NOT NULL,\n PRIMARY KEY(\"stateVersion\", \"pos\"),\n UNIQUE(\"table\", \"rowKey\")\n );\n";
8
+ export declare const CREATE_V9_TABLE_METADATA_TABLE = "\n CREATE TABLE \"_zero.tableMetadata\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"metadata\" TEXT NOT NULL,\n PRIMARY KEY (\"schema\", \"table\")\n );\n";
8
9
  export declare const schemaVersionMigrationMap: IncrementalMigrationMap;
9
10
  //# sourceMappingURL=replica-schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"replica-schema.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAE9D,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AASvC,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,iBAgBf;AAED,eAAO,MAAM,+BAA+B,0UAW3C,CAAC;AAEF,eAAO,MAAM,oBAAoB,8XAUhC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,uBA4EvC,CAAC"}
1
+ {"version":3,"file":"replica-schema.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAE9D,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,+BAA+B,CAAC;AAQvC,wBAAsB,WAAW,CAC/B,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,iBAgBf;AAED,eAAO,MAAM,+BAA+B,0UAW3C,CAAC;AAEF,eAAO,MAAM,oBAAoB,8XAUhC,CAAC;AAEF,eAAO,MAAM,8BAA8B,sMAO1C,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,uBA4FvC,CAAC"}
@@ -4,7 +4,6 @@ import { runSchemaMigrations } from "../../../db/migration-lite.js";
4
4
  import { AutoResetSignal } from "../../change-streamer/schema/tables.js";
5
5
  import { populateFromExistingTables } from "../../replicator/schema/column-metadata.js";
6
6
  import { recordEvent, CREATE_RUNTIME_EVENTS_TABLE } from "../../replicator/schema/replication-state.js";
7
- import { CREATE_TABLE_METADATA_TABLE } from "../../replicator/schema/table-metadata.js";
8
7
  async function initReplica(log, debugName, dbPath, initialSync) {
9
8
  const setupMigration = {
10
9
  migrateSchema: (log2, tx) => initialSync(log2, tx),
@@ -70,6 +69,17 @@ const CREATE_V7_CHANGE_LOG = (
70
69
  );
71
70
  `
72
71
  );
72
+ const CREATE_V9_TABLE_METADATA_TABLE = (
73
+ /*sql*/
74
+ `
75
+ CREATE TABLE "_zero.tableMetadata" (
76
+ "schema" TEXT NOT NULL,
77
+ "table" TEXT NOT NULL,
78
+ "metadata" TEXT NOT NULL,
79
+ PRIMARY KEY ("schema", "table")
80
+ );
81
+ `
82
+ );
73
83
  const schemaVersionMigrationMap = {
74
84
  // There's no incremental migration from v1. Just reset the replica.
75
85
  4: {
@@ -108,7 +118,7 @@ const schemaVersionMigrationMap = {
108
118
  /*sql*/
109
119
  `DELETE FROM "_zero.column_metadata"`
110
120
  );
111
- const tables = listTables(db, false);
121
+ const tables = listTables(db, false, false);
112
122
  populateFromExistingTables(db, tables);
113
123
  }
114
124
  },
@@ -121,7 +131,7 @@ const schemaVersionMigrationMap = {
121
131
  ADD COLUMN "backfillingColumnVersions" TEXT DEFAULT '{}';
122
132
  ALTER TABLE "_zero.column_metadata"
123
133
  ADD COLUMN backfill TEXT;
124
- ` + CREATE_TABLE_METADATA_TABLE
134
+ ` + CREATE_V9_TABLE_METADATA_TABLE
125
135
  );
126
136
  }
127
137
  },
@@ -135,11 +145,30 @@ const schemaVersionMigrationMap = {
135
145
  `
136
146
  );
137
147
  }
148
+ },
149
+ 11: {
150
+ migrateSchema: (_, db) => {
151
+ db.exec(
152
+ /*sql*/
153
+ `
154
+ ALTER TABLE "_zero.tableMetadata"
155
+ ADD COLUMN "minRowVersion" TEXT NOT NULL DEFAULT '00';
156
+
157
+ -- Removing the NOT NULL constraint from "metadata" requires copying
158
+ -- the column. We piggyback the rename to "upstreamMetadata" here.
159
+ ALTER TABLE "_zero.tableMetadata"
160
+ ADD COLUMN "upstreamMetadata" TEXT;
161
+ UPDATE "_zero.tableMetadata" SET "upstreamMetadata" = "metadata";
162
+ ALTER TABLE "_zero.tableMetadata" DROP "metadata";
163
+ `
164
+ );
165
+ }
138
166
  }
139
167
  };
140
168
  export {
141
169
  CREATE_V6_COLUMN_METADATA_TABLE,
142
170
  CREATE_V7_CHANGE_LOG,
171
+ CREATE_V9_TABLE_METADATA_TABLE,
143
172
  initReplica,
144
173
  schemaVersionMigrationMap,
145
174
  upgradeReplica
@@ -1 +1 @@
1
- {"version":3,"file":"replica-schema.js","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {SqliteError} from '@rocicorp/zero-sqlite3';\nimport type {Database} from '../../../../../zqlite/src/db.ts';\nimport {listTables} from '../../../db/lite-tables.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../db/migration-lite.ts';\nimport {AutoResetSignal} from '../../change-streamer/schema/tables.ts';\nimport {populateFromExistingTables} from '../../replicator/schema/column-metadata.ts';\nimport {\n CREATE_RUNTIME_EVENTS_TABLE,\n recordEvent,\n} from '../../replicator/schema/replication-state.ts';\nimport {CREATE_TABLE_METADATA_TABLE} from '../../replicator/schema/table-metadata.ts';\n\nexport async function initReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n initialSync: (lc: LogContext, tx: Database) => Promise<void>,\n): Promise<void> {\n const setupMigration: Migration = {\n migrateSchema: (log, tx) => initialSync(log, tx),\n minSafeVersion: 1,\n };\n\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n setupMigration,\n schemaVersionMigrationMap,\n );\n } catch (e) {\n if (e instanceof SqliteError && e.code === 'SQLITE_CORRUPT') {\n throw new AutoResetSignal(e.message);\n }\n throw e;\n }\n}\n\nexport async function upgradeReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n) {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n // setupMigration should never be invoked\n {\n migrateSchema: () => {\n throw new Error(\n 'This should only be called for already synced replicas',\n );\n },\n },\n schemaVersionMigrationMap,\n );\n}\n\nexport const CREATE_V6_COLUMN_METADATA_TABLE = /*sql*/ `\n CREATE TABLE \"_zero.column_metadata\" (\n table_name TEXT NOT NULL,\n column_name TEXT NOT NULL,\n upstream_type TEXT NOT NULL,\n is_not_null INTEGER NOT NULL,\n is_enum INTEGER NOT NULL,\n is_array INTEGER NOT NULL,\n character_max_length INTEGER,\n PRIMARY KEY (table_name, column_name)\n );\n`;\n\nexport const CREATE_V7_CHANGE_LOG = /*sql*/ `\n CREATE TABLE \"_zero.changeLog2\" (\n \"stateVersion\" TEXT NOT NULL,\n \"pos\" INT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"rowKey\" TEXT NOT NULL,\n \"op\" TEXT NOT NULL,\n PRIMARY KEY(\"stateVersion\", \"pos\"),\n UNIQUE(\"table\", \"rowKey\")\n );\n`;\n\nexport const schemaVersionMigrationMap: IncrementalMigrationMap = {\n // There's no incremental migration from v1. Just reset the replica.\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('upgrading replica to new schema');\n },\n minSafeVersion: 3,\n },\n\n 5: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_RUNTIME_EVENTS_TABLE);\n },\n migrateData: (_, db) => {\n recordEvent(db, 'upgrade');\n },\n },\n\n // Revised in the migration to v8 because the v6 code was incomplete.\n 6: {},\n\n 7: {\n migrateSchema: (_, db) => {\n // Note: The original \"changeLog\" table is kept so that the replica file\n // is compatible with older zero-caches. However, it is truncated for\n // space savings (since historic changes were never read).\n db.exec(`DELETE FROM \"_zero.changeLog\"`);\n // First version of changeLog2\n db.exec(CREATE_V7_CHANGE_LOG);\n },\n },\n\n 8: {\n migrateSchema: (_, db) => {\n const tableExists = db\n .prepare(\n `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`,\n )\n .get();\n\n if (!tableExists) {\n db.exec(CREATE_V6_COLUMN_METADATA_TABLE);\n }\n },\n migrateData: (_, db) => {\n // Re-populate the ColumnMetadataStore; the original migration\n // at v6 was incomplete, as covered replicas migrated from earlier\n // versions but did not initialize the table for new replicas.\n db.exec(/*sql*/ `DELETE FROM \"_zero.column_metadata\"`);\n\n const tables = listTables(db, false);\n populateFromExistingTables(db, tables);\n },\n },\n\n 9: {\n migrateSchema: (_, db) => {\n db.exec(\n /*sql*/ `\n ALTER TABLE \"_zero.changeLog2\" \n ADD COLUMN \"backfillingColumnVersions\" TEXT DEFAULT '{}';\n ALTER TABLE \"_zero.column_metadata\"\n ADD COLUMN backfill TEXT;\n ` + CREATE_TABLE_METADATA_TABLE,\n );\n },\n },\n\n 10: {\n migrateSchema: (_, db) => {\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.replicationConfig\" \n ADD COLUMN \"initialSyncContext\" TEXT DEFAULT '{}';\n `);\n },\n },\n};\n"],"names":["log"],"mappings":";;;;;;;AAiBA,eAAsB,YACpB,KACA,WACA,QACA,aACe;AACf,QAAM,iBAA4B;AAAA,IAChC,eAAe,CAACA,MAAK,OAAO,YAAYA,MAAK,EAAE;AAAA,IAC/C,gBAAgB;AAAA,EAAA;AAGlB,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,SAAS,GAAG;AACV,QAAI,aAAa,eAAe,EAAE,SAAS,kBAAkB;AAC3D,YAAM,IAAI,gBAAgB,EAAE,OAAO;AAAA,IACrC;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eACpB,KACA,WACA,QACA;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,MACE,eAAe,MAAM;AACnB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAEO,MAAM;AAAA;AAAA,EAA0C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAahD,MAAM;AAAA;AAAA,EAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYrC,MAAM,4BAAqD;AAAA;AAAA,EAEhE,GAAG;AAAA,IACD,eAAe,MAAM;AACnB,YAAM,IAAI,gBAAgB,iCAAiC;AAAA,IAC7D;AAAA,IACA,gBAAgB;AAAA,EAAA;AAAA,EAGlB,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,2BAA2B;AAAA,IACrC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AACtB,kBAAY,IAAI,SAAS;AAAA,IAC3B;AAAA,EAAA;AAAA;AAAA,EAIF,GAAG,CAAA;AAAA,EAEH,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AAIxB,SAAG,KAAK,+BAA+B;AAEvC,SAAG,KAAK,oBAAoB;AAAA,IAC9B;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,YAAM,cAAc,GACjB;AAAA,QACC;AAAA,MAAA,EAED,IAAA;AAEH,UAAI,CAAC,aAAa;AAChB,WAAG,KAAK,+BAA+B;AAAA,MACzC;AAAA,IACF;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AAItB,SAAG;AAAA;AAAA,QAAa;AAAA,MAAA;AAEhB,YAAM,SAAS,WAAW,IAAI,KAAK;AACnC,iCAA2B,IAAI,MAAM;AAAA,IACvC;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QACO;AAAA;AAAA;AAAA;AAAA;AAAA,UAKN;AAAA,MAAA;AAAA,IAEN;AAAA,EAAA;AAAA,EAGF,IAAI;AAAA,IACF,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QAAa;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,IAIlB;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"replica-schema.js","sources":["../../../../../../../zero-cache/src/services/change-source/common/replica-schema.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {SqliteError} from '@rocicorp/zero-sqlite3';\nimport type {Database} from '../../../../../zqlite/src/db.ts';\nimport {listTables} from '../../../db/lite-tables.ts';\nimport {\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../db/migration-lite.ts';\nimport {AutoResetSignal} from '../../change-streamer/schema/tables.ts';\nimport {populateFromExistingTables} from '../../replicator/schema/column-metadata.ts';\nimport {\n CREATE_RUNTIME_EVENTS_TABLE,\n recordEvent,\n} from '../../replicator/schema/replication-state.ts';\n\nexport async function initReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n initialSync: (lc: LogContext, tx: Database) => Promise<void>,\n): Promise<void> {\n const setupMigration: Migration = {\n migrateSchema: (log, tx) => initialSync(log, tx),\n minSafeVersion: 1,\n };\n\n try {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n setupMigration,\n schemaVersionMigrationMap,\n );\n } catch (e) {\n if (e instanceof SqliteError && e.code === 'SQLITE_CORRUPT') {\n throw new AutoResetSignal(e.message);\n }\n throw e;\n }\n}\n\nexport async function upgradeReplica(\n log: LogContext,\n debugName: string,\n dbPath: string,\n) {\n await runSchemaMigrations(\n log,\n debugName,\n dbPath,\n // setupMigration should never be invoked\n {\n migrateSchema: () => {\n throw new Error(\n 'This should only be called for already synced replicas',\n );\n },\n },\n schemaVersionMigrationMap,\n );\n}\n\nexport const CREATE_V6_COLUMN_METADATA_TABLE = /*sql*/ `\n CREATE TABLE \"_zero.column_metadata\" (\n table_name TEXT NOT NULL,\n column_name TEXT NOT NULL,\n upstream_type TEXT NOT NULL,\n is_not_null INTEGER NOT NULL,\n is_enum INTEGER NOT NULL,\n is_array INTEGER NOT NULL,\n character_max_length INTEGER,\n PRIMARY KEY (table_name, column_name)\n );\n`;\n\nexport const CREATE_V7_CHANGE_LOG = /*sql*/ `\n CREATE TABLE \"_zero.changeLog2\" (\n \"stateVersion\" TEXT NOT NULL,\n \"pos\" INT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"rowKey\" TEXT NOT NULL,\n \"op\" TEXT NOT NULL,\n PRIMARY KEY(\"stateVersion\", \"pos\"),\n UNIQUE(\"table\", \"rowKey\")\n );\n`;\n\nexport const CREATE_V9_TABLE_METADATA_TABLE = /*sql*/ `\n CREATE TABLE \"_zero.tableMetadata\" (\n \"schema\" TEXT NOT NULL,\n \"table\" TEXT NOT NULL,\n \"metadata\" TEXT NOT NULL,\n PRIMARY KEY (\"schema\", \"table\")\n );\n`;\n\nexport const schemaVersionMigrationMap: IncrementalMigrationMap = {\n // There's no incremental migration from v1. Just reset the replica.\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('upgrading replica to new schema');\n },\n minSafeVersion: 3,\n },\n\n 5: {\n migrateSchema: (_, db) => {\n db.exec(CREATE_RUNTIME_EVENTS_TABLE);\n },\n migrateData: (_, db) => {\n recordEvent(db, 'upgrade');\n },\n },\n\n // Revised in the migration to v8 because the v6 code was incomplete.\n 6: {},\n\n 7: {\n migrateSchema: (_, db) => {\n // Note: The original \"changeLog\" table is kept so that the replica file\n // is compatible with older zero-caches. However, it is truncated for\n // space savings (since historic changes were never read).\n db.exec(`DELETE FROM \"_zero.changeLog\"`);\n // First version of changeLog2\n db.exec(CREATE_V7_CHANGE_LOG);\n },\n },\n\n 8: {\n migrateSchema: (_, db) => {\n const tableExists = db\n .prepare(\n `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`,\n )\n .get();\n\n if (!tableExists) {\n db.exec(CREATE_V6_COLUMN_METADATA_TABLE);\n }\n },\n migrateData: (_, db) => {\n // Re-populate the ColumnMetadataStore; the original migration\n // at v6 was incomplete, as covered replicas migrated from earlier\n // versions but did not initialize the table for new replicas.\n db.exec(/*sql*/ `DELETE FROM \"_zero.column_metadata\"`);\n\n const tables = listTables(db, false, false);\n populateFromExistingTables(db, tables);\n },\n },\n\n 9: {\n migrateSchema: (_, db) => {\n db.exec(\n /*sql*/ `\n ALTER TABLE \"_zero.changeLog2\" \n ADD COLUMN \"backfillingColumnVersions\" TEXT DEFAULT '{}';\n ALTER TABLE \"_zero.column_metadata\"\n ADD COLUMN backfill TEXT;\n ` + CREATE_V9_TABLE_METADATA_TABLE,\n );\n },\n },\n\n 10: {\n migrateSchema: (_, db) => {\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.replicationConfig\" \n ADD COLUMN \"initialSyncContext\" TEXT DEFAULT '{}';\n `);\n },\n },\n\n 11: {\n migrateSchema: (_, db) => {\n db.exec(/*sql*/ `\n ALTER TABLE \"_zero.tableMetadata\"\n ADD COLUMN \"minRowVersion\" TEXT NOT NULL DEFAULT '00';\n\n -- Removing the NOT NULL constraint from \"metadata\" requires copying\n -- the column. We piggyback the rename to \"upstreamMetadata\" here.\n ALTER TABLE \"_zero.tableMetadata\"\n ADD COLUMN \"upstreamMetadata\" TEXT;\n UPDATE \"_zero.tableMetadata\" SET \"upstreamMetadata\" = \"metadata\";\n ALTER TABLE \"_zero.tableMetadata\" DROP \"metadata\";\n `);\n },\n },\n};\n"],"names":["log"],"mappings":";;;;;;AAgBA,eAAsB,YACpB,KACA,WACA,QACA,aACe;AACf,QAAM,iBAA4B;AAAA,IAChC,eAAe,CAACA,MAAK,OAAO,YAAYA,MAAK,EAAE;AAAA,IAC/C,gBAAgB;AAAA,EAAA;AAGlB,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,SAAS,GAAG;AACV,QAAI,aAAa,eAAe,EAAE,SAAS,kBAAkB;AAC3D,YAAM,IAAI,gBAAgB,EAAE,OAAO;AAAA,IACrC;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eACpB,KACA,WACA,QACA;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,MACE,eAAe,MAAM;AACnB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAEO,MAAM;AAAA;AAAA,EAA0C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAahD,MAAM;AAAA;AAAA,EAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYrC,MAAM;AAAA;AAAA,EAAyC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS/C,MAAM,4BAAqD;AAAA;AAAA,EAEhE,GAAG;AAAA,IACD,eAAe,MAAM;AACnB,YAAM,IAAI,gBAAgB,iCAAiC;AAAA,IAC7D;AAAA,IACA,gBAAgB;AAAA,EAAA;AAAA,EAGlB,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG,KAAK,2BAA2B;AAAA,IACrC;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AACtB,kBAAY,IAAI,SAAS;AAAA,IAC3B;AAAA,EAAA;AAAA;AAAA,EAIF,GAAG,CAAA;AAAA,EAEH,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AAIxB,SAAG,KAAK,+BAA+B;AAEvC,SAAG,KAAK,oBAAoB;AAAA,IAC9B;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,YAAM,cAAc,GACjB;AAAA,QACC;AAAA,MAAA,EAED,IAAA;AAEH,UAAI,CAAC,aAAa;AAChB,WAAG,KAAK,+BAA+B;AAAA,MACzC;AAAA,IACF;AAAA,IACA,aAAa,CAAC,GAAG,OAAO;AAItB,SAAG;AAAA;AAAA,QAAa;AAAA,MAAA;AAEhB,YAAM,SAAS,WAAW,IAAI,OAAO,KAAK;AAC1C,iCAA2B,IAAI,MAAM;AAAA,IACvC;AAAA,EAAA;AAAA,EAGF,GAAG;AAAA,IACD,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QACO;AAAA;AAAA;AAAA;AAAA;AAAA,UAKN;AAAA,MAAA;AAAA,IAEN;AAAA,EAAA;AAAA,EAGF,IAAI;AAAA,IACF,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QAAa;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,IAIlB;AAAA,EAAA;AAAA,EAGF,IAAI;AAAA,IACF,eAAe,CAAC,GAAG,OAAO;AACxB,SAAG;AAAA;AAAA,QAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,IAWlB;AAAA,EAAA;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"backfill-stream.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/backfill-stream.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAWjD,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAGf,eAAe,EAChB,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,mBAAmB,CAAC;AAI/C,KAAK,aAAa,GAAG;IACnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAOF;;;;;GAKG;AACH,wBAAuB,cAAc,CACnC,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,EAAC,IAAI,EAAE,YAAY,EAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,EAC5D,EAAE,EAAE,eAAe,EACnB,IAAI,GAAE,aAAkB,GACvB,cAAc,CAAC,eAAe,GAAG,iBAAiB,CAAC,CAyDrD"}
1
+ {"version":3,"file":"backfill-stream.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/backfill-stream.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAWjD,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EAGf,eAAe,EAChB,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,mBAAmB,CAAC;AAI/C,KAAK,aAAa,GAAG;IACnB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAOF;;;;;GAKG;AACH,wBAAuB,cAAc,CACnC,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,EAAC,IAAI,EAAE,YAAY,EAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,EAC5D,EAAE,EAAE,eAAe,EACnB,IAAI,GAAE,aAAkB,GACvB,cAAc,CAAC,eAAe,GAAG,iBAAiB,CAAC,CA6DrD"}