@embracesql/postgres
Advanced tools
Comparing version
{ | ||
"name": "@embracesql/postgres", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "EmbraceSQL shared library for talking to postgres. Used in Node.", | ||
@@ -13,3 +13,3 @@ "type": "module", | ||
"dependencies": { | ||
"@embracesql/shared": "^0.1.0", | ||
"@embracesql/shared": "^0.1.1", | ||
"glob": "^10.3.10", | ||
@@ -23,3 +23,3 @@ "object-hash": "^3.0.0", | ||
}, | ||
"gitHead": "6f0191319a0be4f5ebddc92fe81fb0b729521e87" | ||
"gitHead": "8ea204346f819dd1a7cae4e0380f211b89eee243" | ||
} |
import { PGIndexes } from "./generator/pgtype/pgindex"; | ||
import { PGNamespace } from "./generator/pgtype/pgnamespace"; | ||
import { PGProcs } from "./generator/pgtype/pgproc/pgproc"; | ||
import { PGSettings } from "./generator/pgtype/pgsettings"; | ||
import { PGTables } from "./generator/pgtype/pgtable"; | ||
@@ -141,2 +142,5 @@ import { PGTypes } from "./generator/pgtype/pgtype"; | ||
}; | ||
// settings from the database | ||
const settings = await PGSettings.factory(sql); | ||
settings.loadAST(generationContext); | ||
@@ -263,2 +267,5 @@ // start off with the types grouped into namespaces, types will be | ||
database, | ||
settings: Object.fromEntries( | ||
database.settings.map((s) => [s.typescriptPropertyName, s.setting]), | ||
), | ||
}; | ||
@@ -265,0 +272,0 @@ |
@@ -22,1 +22,2 @@ const operatorOverrides = new Map<string, string>(); | ||
registerOperatorOverride("gist_trgm_ops", "%"); | ||
registerOperatorOverride("tsvector_ops", "@@"); |
@@ -0,4 +1,5 @@ | ||
import { Context } from "../../../../context"; | ||
import { PGCatalogType } from "../../pgcatalogtype"; | ||
import { registerOverride } from "../_overrides"; | ||
import { GenerationContext } from "@embracesql/shared"; | ||
import { GenerationContext, Geometry } from "@embracesql/shared"; | ||
@@ -8,11 +9,22 @@ class PGTypeBox extends PGCatalogType { | ||
console.assert(context); | ||
return `Geometry.Box`; | ||
} | ||
typescriptTypeParser(context: GenerationContext) { | ||
console.assert(context); | ||
return ` | ||
{ | ||
upperRight: Point; | ||
lowerLeft: Point; | ||
} | ||
return Geometry.parseBox(from); | ||
`; | ||
} | ||
serializeToPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.serializeBox(x); | ||
} | ||
parseFromPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.parseBox(x as string); | ||
} | ||
} | ||
registerOverride("box", PGTypeBox); |
@@ -0,4 +1,5 @@ | ||
import { Context } from "../../../../context"; | ||
import { PGTypeBase } from "../../pgtypebase"; | ||
import { registerOverride } from "../_overrides"; | ||
import { GenerationContext } from "@embracesql/shared"; | ||
import { GenerationContext, Geometry } from "@embracesql/shared"; | ||
@@ -8,11 +9,22 @@ class PGTypeCircle extends PGTypeBase { | ||
console.assert(context); | ||
return `Geometry.Circle`; | ||
} | ||
typescriptTypeParser(context: GenerationContext) { | ||
console.assert(context); | ||
return ` | ||
{ | ||
center: Point; | ||
radius: number; | ||
} | ||
return Geometry.parseCircle(from); | ||
`; | ||
} | ||
serializeToPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.serializeCircle(x); | ||
} | ||
parseFromPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.parseCircle(x as string); | ||
} | ||
} | ||
registerOverride("circle", PGTypeCircle); |
@@ -0,4 +1,5 @@ | ||
import { Context } from "../../../../context"; | ||
import { PGCatalogType } from "../../pgcatalogtype"; | ||
import { registerOverride } from "../_overrides"; | ||
import { GenerationContext } from "@embracesql/shared"; | ||
import { GenerationContext, Geometry } from "@embracesql/shared"; | ||
@@ -8,12 +9,22 @@ class PGLine extends PGCatalogType { | ||
console.assert(context); | ||
return `Geometry.Line`; | ||
} | ||
typescriptTypeParser(context: GenerationContext) { | ||
console.assert(context); | ||
return ` | ||
{ | ||
a: number; | ||
b: number; | ||
c: number; | ||
} | ||
return Geometry.parseLine(from); | ||
`; | ||
} | ||
serializeToPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.serializeLine(x); | ||
} | ||
parseFromPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.parseLine(x as string); | ||
} | ||
} | ||
registerOverride("line", PGLine); |
@@ -0,4 +1,5 @@ | ||
import { Context } from "../../../../context"; | ||
import { PGCatalogType } from "../../pgcatalogtype"; | ||
import { registerOverride } from "../_overrides"; | ||
import { GenerationContext } from "@embracesql/shared"; | ||
import { GenerationContext, Geometry } from "@embracesql/shared"; | ||
@@ -8,11 +9,22 @@ class PGLineSegment extends PGCatalogType { | ||
console.assert(context); | ||
return `Geometry.LineSegment`; | ||
} | ||
typescriptTypeParser(context: GenerationContext) { | ||
console.assert(context); | ||
return ` | ||
{ | ||
from: Point; | ||
to: Point; | ||
} | ||
return Geometry.parseLineSegment(from); | ||
`; | ||
} | ||
serializeToPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.serializeLineSegment(x); | ||
} | ||
parseFromPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.parseLineSegment(x as string); | ||
} | ||
} | ||
registerOverride("lseg", PGLineSegment); |
@@ -0,4 +1,5 @@ | ||
import { Context } from "../../../../context"; | ||
import { PGCatalogType } from "../../pgcatalogtype"; | ||
import { registerOverride } from "../_overrides"; | ||
import { GenerationContext } from "@embracesql/shared"; | ||
import { GenerationContext, Geometry } from "@embracesql/shared"; | ||
@@ -8,20 +9,22 @@ class PGTypePoint extends PGCatalogType { | ||
console.assert(context); | ||
return `Geometry.Point`; | ||
} | ||
typescriptTypeParser(context: GenerationContext) { | ||
console.assert(context); | ||
return ` | ||
{ | ||
x: number; | ||
y: number; | ||
} | ||
return Geometry.parsePoint(from); | ||
`; | ||
} | ||
} | ||
class PGTypePointArray extends PGCatalogType { | ||
typescriptTypeDefinition(context: GenerationContext) { | ||
serializeToPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return `Array<Point>`; | ||
return Geometry.serializePoint(x); | ||
} | ||
parseFromPostgres(context: Context, x: unknown) { | ||
console.assert(context); | ||
return Geometry.parsePoint(x as string); | ||
} | ||
} | ||
registerOverride("point", PGTypePoint); | ||
registerOverride("path", PGTypePointArray); | ||
registerOverride("polygon", PGTypePointArray); |
@@ -6,7 +6,5 @@ import { PGTypeNumber } from "../base/number"; | ||
// yeah -- looks odd -- but registerOverride needs to be defined first | ||
export * from "./uuid"; | ||
// TODO: geometric types need real use cases and parsers to match docs at | ||
// https://www.postgresql.org/docs/current/datatype-geometric.html | ||
export * from "./geometric/point"; | ||
export * from "./geometric/path"; | ||
export * from "./geometric/box"; | ||
@@ -20,3 +18,5 @@ export * from "./geometric/circle"; | ||
export * from "./cube"; | ||
export * from "./postgis"; | ||
export * from "./tsvector"; | ||
registerOverride("interval", PGTypeNumber); |
@@ -1,5 +0,2 @@ | ||
import { Context } from "../../context"; | ||
import { groupBy } from "../../util"; | ||
import { cleanIdentifierForTypescript } from "@embracesql/shared"; | ||
import { camelCase } from "change-case"; | ||
import path from "path"; | ||
@@ -76,33 +73,2 @@ import { Sql } from "postgres"; | ||
} | ||
get typescriptName() { | ||
// camel case -- this is a 'property like' | ||
return `${camelCase(cleanIdentifierForTypescript(this.attribute.attname))}`; | ||
} | ||
get postgresName() { | ||
return this.attribute.attname; | ||
} | ||
/** | ||
* Render a code generation string that will create a postgres 'right hand side' | ||
* of an equals value expression for this attribute. | ||
* | ||
* This will create an expression that will self equal for undefined on the | ||
* parameterHolder in calling typescript -- allows partial updates. | ||
* | ||
*/ | ||
postgresValueExpression( | ||
context: Context, | ||
parameterHolder = "parameters", | ||
selfEqual = true, | ||
) { | ||
const postgresType = context.resolveType(this.attribute.atttypid); | ||
const undefinedExpression = selfEqual | ||
? `sql("${this.postgresName}")` | ||
: "sql`DEFAULT`"; | ||
const valueExpression = `typed[${postgresType.oid}](${parameterHolder}.${this.typescriptName})`; | ||
const combinedExpression = `${parameterHolder}.${this.typescriptName} === undefined ? ${undefinedExpression} : ${valueExpression}`; | ||
return `\${ ${combinedExpression} }`; | ||
} | ||
} |
@@ -69,2 +69,13 @@ import { Context, PostgresTypecast } from "../../context"; | ||
/** | ||
* Wrap a a WHERE predicate right hand side parameter expression | ||
* with additional postgres side function calls. | ||
* | ||
* Really useful for full text search. | ||
*/ | ||
postgresWrapReadParameter(context: GenerationContext, expression: string) { | ||
console.assert(context); | ||
return expression; | ||
} | ||
/** | ||
* Given a value, turn it into postgres protocol serialization format | ||
@@ -91,2 +102,9 @@ * for use with the postgres driver. | ||
/** | ||
* Override this to change the postgres type id of the serialized result. | ||
*/ | ||
get toOID() { | ||
return this.oid; | ||
} | ||
/** | ||
* Build up the postgres type casting capability. This is used by the postgres | ||
@@ -97,3 +115,3 @@ * driver to go to and from the database. | ||
return { | ||
to: this.oid, | ||
to: this.toOID, | ||
from: [this.oid], | ||
@@ -104,2 +122,12 @@ serialize: (x) => this.serializeToPostgres(context, x), | ||
} | ||
/** | ||
* Build up code to provide additional type options. | ||
*/ | ||
typescriptTypeOptions(context: Context): string { | ||
console.assert(context); | ||
return ` | ||
export type Options = never; | ||
`; | ||
} | ||
} |
@@ -146,9 +146,2 @@ import { PGTypeBool } from "./base/bool"; | ||
); | ||
case "tsvector": | ||
return new PGTypeText( | ||
catalog.oid, | ||
catalog.nspname, | ||
catalog.typname, | ||
"", | ||
); | ||
case "gtsvector": | ||
@@ -161,9 +154,2 @@ return new PGTypeTextArray( | ||
); | ||
case "tsquery": | ||
return new PGTypeText( | ||
catalog.oid, | ||
catalog.nspname, | ||
catalog.typname, | ||
"", | ||
); | ||
case "uri": | ||
@@ -170,0 +156,0 @@ return new PGTypeUri(catalog.oid, catalog.nspname, catalog.typname, ""); |
@@ -73,6 +73,2 @@ import { Context } from "../../context"; | ||
get sqlColumns() { | ||
return this.attributes.map((a) => a.postgresName).join(","); | ||
} | ||
get postgresToTypescript() { | ||
@@ -79,0 +75,0 @@ // snippet will pick resultset fields to type map |
import { postgresToTypescript, postgresValueExpression } from "./shared"; | ||
import { | ||
CreateOperationNode, | ||
GenerationContext, | ||
VALUES, | ||
} from "@embracesql/shared"; | ||
import { camelCase } from "change-case"; | ||
import { CreateOperationNode, GenerationContext } from "@embracesql/shared"; | ||
@@ -14,8 +9,8 @@ /** | ||
async before(context: GenerationContext, node: CreateOperationNode) { | ||
const valuesType = `Partial<${node.table.type.typescriptNamespacedName}>`; | ||
const optionType = `${node.table.typescriptNamespacedName}.Options`; | ||
const generationBuffer = [""]; | ||
generationBuffer.push( | ||
`async create(${camelCase(VALUES)}: ${ | ||
node.table.typescriptNamespacedName | ||
}.Values): Promise<${node.table.type.typescriptNamespacedName}>{`, | ||
`async create(values: ${valuesType}, options?: ${optionType}): Promise<${node.table.type.typescriptNamespacedName}>{`, | ||
); | ||
@@ -42,5 +37,3 @@ generationBuffer.push( | ||
generationBuffer.push(` | ||
if (!${ | ||
node.table.typescriptNamespacedName | ||
}.includesPrimaryKey(${camelCase(VALUES)})) { | ||
if (!${node.table.typescriptNamespacedName}.includesPrimaryKey(values)) { | ||
`); | ||
@@ -52,3 +45,3 @@ const sql = ` | ||
VALUES (${node.table.columnsNotInPrimaryKey | ||
.map((a) => postgresValueExpression(context, a, VALUES)) | ||
.map((a) => postgresValueExpression(context, a, "values")) | ||
.join(",")}) | ||
@@ -73,3 +66,3 @@ RETURNING | ||
VALUES (${node.table.type.attributes | ||
.map((a) => postgresValueExpression(context, a, VALUES)) | ||
.map((a) => postgresValueExpression(context, a, "values")) | ||
.join(",")}) | ||
@@ -76,0 +69,0 @@ ON CONFLICT (${node.table.columnsInPrimaryKey |
import { postgresToTypescript, sqlPredicate } from "./shared"; | ||
import { | ||
PARAMETERS, | ||
DeleteOperationNode, | ||
GenerationContext, | ||
} from "@embracesql/shared"; | ||
import { DeleteOperationNode, GenerationContext } from "@embracesql/shared"; | ||
@@ -18,3 +14,5 @@ /** | ||
async before(context: GenerationContext, node: DeleteOperationNode) { | ||
const parameters = `${PARAMETERS}: ${node.index.type.typescriptNamespacedName}`; | ||
const parameters = `parameters: ${node.index.type.typescriptNamespacedName}`; | ||
const optionType = `${node.index.type.typescriptNamespacedName}.Options & ${node.index.table.typescriptNamespacedName}.Options`; | ||
const options = `options?: ${optionType}`; | ||
const sqlColumnNames = node.index.table.type.attributes | ||
@@ -29,7 +27,7 @@ .map((a) => a.name) | ||
WHERE | ||
${sqlPredicate(context, node.index, PARAMETERS)} | ||
${sqlPredicate(context, node.index, "parameters")} | ||
RETURNING ${sqlColumnNames}`; | ||
return [ | ||
`async ${node.typescriptPropertyName}(${parameters}) {`, | ||
`async ${node.typescriptPropertyName}(${parameters}, ${options}) {`, | ||
` console.assert(parameters);`, | ||
@@ -36,0 +34,0 @@ ` const sql = this.database.context.sql;`, |
@@ -17,3 +17,4 @@ import { postgresToTypescript, sqlPredicate } from "./shared"; | ||
const parameters = `${PARAMETERS}: ${node.index.type.typescriptNamespacedName}`; | ||
const options = `options?: ${node.index.table.typescriptNamespacedName}.Options`; | ||
const optionType = `${node.index.type.typescriptNamespacedName}.Options & ${node.index.table.typescriptNamespacedName}.Options`; | ||
const options = `options?: ${optionType}`; | ||
const returns = node.index.unique | ||
@@ -20,0 +21,0 @@ ? `Promise<${node.index.table.type.typescriptNamespacedName}>` |
@@ -104,6 +104,8 @@ /** | ||
(a, i) => | ||
`${a.name} ${node.operators[i]} ${postgresValueExpression( | ||
context, | ||
a, | ||
holder, | ||
`${a.name} ${node.operators[i]} ${a.type.postgresWrapReadParameter( | ||
{ | ||
...context, | ||
currentSymbolName: `options?.${a.typescriptPropertyName}`, | ||
}, | ||
postgresValueExpression(context, a, holder), | ||
)}`, | ||
@@ -110,0 +112,0 @@ ) |
@@ -25,2 +25,4 @@ import { | ||
const parameters = `${PARAMETERS}: ${node.index.type.typescriptNamespacedName}, ${VALUES}: Partial<${node.index.table.typescriptNamespacedName}.Values>`; | ||
const optionType = `${node.index.type.typescriptNamespacedName}.Options & ${node.index.table.typescriptNamespacedName}.Options`; | ||
const options = `options?: ${optionType}`; | ||
const returns = node.index.unique | ||
@@ -34,3 +36,3 @@ ? `Promise<${node.index.table.type.typescriptNamespacedName}>` | ||
generationBuffer.push( | ||
`async ${node.typescriptPropertyName}(${parameters}) : ${returns}{`, | ||
`async ${node.typescriptPropertyName}(${parameters}, ${options}) : ${returns}{`, | ||
); | ||
@@ -37,0 +39,0 @@ generationBuffer.push( |
@@ -71,3 +71,4 @@ import { AllOperation } from "./autocrud/all"; | ||
`export class Database extends PostgresDatabase implements HasDatabase { `, | ||
`get database() { return this};`, | ||
`get database() { return this };`, | ||
`get settings() { return this.context.settings as Settings };`, | ||
` | ||
@@ -74,0 +75,0 @@ /** |
@@ -5,4 +5,6 @@ import { GenerationContext } from ".."; | ||
import { generatePrimaryKeyPickers } from "./generatePrimaryKeyPickers"; | ||
import { generateSettings } from "./generateSettings"; | ||
import { generateTypeComparison } from "./generateTypeComparison"; | ||
import { generateTypeGuards } from "./generateTypeGuards"; | ||
import { generateTypeOptions } from "./generateTypeOptions"; | ||
import { generateTypeParsers } from "./generateTypeParsers"; | ||
@@ -102,2 +104,3 @@ import { | ||
import type { PartiallyOptional, PossiblyEmpty, ReadOptions, Sort } from "@embracesql/shared"; | ||
import { Geometry } from "@embracesql/shared"; | ||
@@ -125,9 +128,2 @@ `, | ||
handlers: { | ||
[ASTKind.Database]: { | ||
before: async () => { | ||
return [ | ||
// schema wide shared | ||
].join("\n"); | ||
}, | ||
}, | ||
[ASTKind.Schema]: NamespaceVisitor, | ||
@@ -174,3 +170,5 @@ [ASTKind.Types]: NamespaceVisitor, | ||
node.type.typescriptNamespacedName | ||
}, Optional & PrimaryKey>`, | ||
}, ${ | ||
node.optionalColumns.length ? "Optional &" : "" | ||
} PrimaryKey>`, | ||
// read options exist per table | ||
@@ -255,4 +253,6 @@ `export ${sortOptions(node.allColumns)};`, | ||
generationBuffer.push(await generateTypeComparison(context)); | ||
generationBuffer.push(await generateTypeOptions(context)); | ||
generationBuffer.push(await generateSettings(context)); | ||
return generationBuffer.join("\n"); | ||
}; |
@@ -23,6 +23,10 @@ import { | ||
// these are flattened names -- no namespacing | ||
context.handlers = { | ||
[ASTKind.Database]: { | ||
before: async () => { | ||
return ` | ||
return await context.database.visit({ | ||
...context, | ||
// include all schemas -- need those built in types | ||
skipSchemas: [], | ||
handlers: { | ||
[ASTKind.Database]: { | ||
before: async () => { | ||
return ` | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -35,15 +39,14 @@ type ArgumentToPostgres = any; | ||
`; | ||
}, | ||
after: async () => { | ||
return `}`; | ||
}, | ||
}, | ||
after: async () => { | ||
return `}`; | ||
}, | ||
[ASTKind.Type]: TypecastEntry, | ||
[ASTKind.Enum]: TypecastEntry, | ||
[ASTKind.CompositeType]: TypecastEntry, | ||
[ASTKind.DomainType]: TypecastEntry, | ||
[ASTKind.ArrayType]: TypecastEntry, | ||
}, | ||
[ASTKind.Type]: TypecastEntry, | ||
[ASTKind.Enum]: TypecastEntry, | ||
[ASTKind.CompositeType]: TypecastEntry, | ||
[ASTKind.DomainType]: TypecastEntry, | ||
[ASTKind.ArrayType]: TypecastEntry, | ||
}; | ||
// include all schemas -- need those built in types | ||
return await context.database.visit({ ...context, skipSchemas: [] }); | ||
}); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
133854
8.01%66
11.86%3936
6.64%Updated