@embracesql/postgres
Advanced tools
Comparing version 0.0.14 to 0.1.0
{ | ||
"name": "@embracesql/postgres", | ||
"version": "0.0.14", | ||
"version": "0.1.0", | ||
"description": "EmbraceSQL shared library for talking to postgres. Used in Node.", | ||
@@ -13,3 +13,3 @@ "type": "module", | ||
"dependencies": { | ||
"@embracesql/shared": "^0.0.14", | ||
"@embracesql/shared": "^0.1.0", | ||
"glob": "^10.3.10", | ||
@@ -23,3 +23,3 @@ "object-hash": "^3.0.0", | ||
}, | ||
"gitHead": "4f59f194e44a54194d5f8d1344a1c0adadfd4958" | ||
"gitHead": "6f0191319a0be4f5ebddc92fe81fb0b729521e87" | ||
} |
@@ -7,2 +7,4 @@ import { Context } from "../context"; | ||
export { generateSchemaDefinitions } from "./typescript/generateSchemaDefinitions"; | ||
export { registerOperatorOverride } from "./pgoperator"; | ||
export { registerOverride } from "./pgtype/overrides/_overrides"; | ||
@@ -9,0 +11,0 @@ /** |
@@ -75,3 +75,2 @@ import { Context, PostgresTypecast } from "../../context"; | ||
// default is just 'a string of it' | ||
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
if (x === null) return null; | ||
@@ -86,6 +85,4 @@ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
parseFromPostgres(context: Context, x: unknown) { | ||
// default is just to echo it -- which is almost never correct | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return | ||
return x; | ||
@@ -103,3 +100,2 @@ } | ||
serialize: (x) => this.serializeToPostgres(context, x), | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return | ||
parse: (x) => this.parseFromPostgres(context, x), | ||
@@ -106,0 +102,0 @@ }; |
import { groupBy } from "../../util"; | ||
import { operatorFor } from "../pgoperator"; | ||
import { PGTypes } from "./pgtype"; | ||
@@ -16,2 +17,3 @@ import { GenerationContext, IndexNode, TableNode } from "@embracesql/shared"; | ||
name: string; | ||
operators: string[]; | ||
}; | ||
@@ -29,11 +31,11 @@ | ||
)) as unknown as IndexRow[]; | ||
return new PGIndexes(context, indexRows); | ||
return new PGIndexes(indexRows); | ||
} | ||
indexesByTableTypeOid: Record<number, PGIndex[]>; | ||
private constructor(context: PGIndexesContext, indexRows: IndexRow[]) { | ||
private constructor(indexRows: IndexRow[]) { | ||
this.indexesByTableTypeOid = groupBy( | ||
indexRows, | ||
(r) => r.tabletypeoid, | ||
(r) => new PGIndex(context, r), | ||
(r) => new PGIndex(r), | ||
); | ||
@@ -49,6 +51,3 @@ } | ||
export class PGIndex { | ||
constructor( | ||
context: PGIndexesContext, | ||
public index: IndexRow, | ||
) {} | ||
constructor(public index: IndexRow) {} | ||
@@ -62,4 +61,5 @@ loadAST(context: GenerationContext, table: TableNode) { | ||
context.database.resolveType(this.index.indexrelid), | ||
this.index.operators.map(operatorFor), | ||
); | ||
} | ||
} |
@@ -103,30 +103,32 @@ import { Context, PostgresProcTypecast } from "../../../context"; | ||
// inputs -- which may have no attributes | ||
// this type won't exist in the database catalog - we're treating | ||
// all the parameters which are in a flat argument list style | ||
// as a structured object single 'parameter' | ||
const parametersType = new CompositeTypeNode( | ||
PARAMETERS, | ||
node, | ||
"", // no identifier, this is not a type in the database | ||
); | ||
if (proc.proc.proargtypes.length > 0) { | ||
// inputs -- which are not required because of no-arg functions | ||
// this type won't exist in the database catalog - we're treating | ||
// all the parameters which are in a flat argument list style | ||
// as a structured object single 'parameter' | ||
const parametersType = new CompositeTypeNode( | ||
PARAMETERS, | ||
node, | ||
"", // no identifier, this is not a type in the database | ||
); | ||
proc.proc.proargtypes | ||
.flatMap((t) => t) | ||
.forEach((oid, i) => { | ||
const type = context.database.resolveType(oid)!; | ||
new AttributeNode( | ||
parametersType, | ||
proc.proc.proargnames[i] | ||
? proc.proc.proargnames[i] | ||
: `argument_${i}`, | ||
i, | ||
type, | ||
i > proc.proc.proargtypes.length - proc.proc.pronargdefaults, | ||
true, | ||
proc.proc.proargnames[i] !== undefined, | ||
); | ||
}); | ||
// inputs | ||
new ParametersNode(node, parametersType); | ||
proc.proc.proargtypes | ||
.flatMap((t) => t) | ||
.forEach((oid, i) => { | ||
const type = context.database.resolveType(oid)!; | ||
new AttributeNode( | ||
parametersType, | ||
proc.proc.proargnames[i] | ||
? proc.proc.proargnames[i] | ||
: `argument_${i}`, | ||
i, | ||
type, | ||
i > proc.proc.proargtypes.length - proc.proc.pronargdefaults, | ||
true, | ||
proc.proc.proargnames[i] !== undefined, | ||
); | ||
}); | ||
// inputs | ||
new ParametersNode(node, parametersType); | ||
} | ||
} | ||
@@ -133,0 +135,0 @@ } |
@@ -10,3 +10,2 @@ import { Context } from "../../context"; | ||
GenerationContext, | ||
cleanIdentifierForTypescript, | ||
compositeAttribute, | ||
@@ -75,20 +74,2 @@ escapeCompositeValue, | ||
typescriptTypeDefinition(context: GenerationContext) { | ||
// all the fields -- and a partial type to allow filling out with | ||
// various sub selects | ||
const nameAndType = this.attributes.map((a) => { | ||
const attributeType = context.database.resolveType(a.attribute.atttypid)!; | ||
const attributeName = `${camelCase( | ||
cleanIdentifierForTypescript(a.attribute.attname), | ||
)}`; | ||
const attributeTypeName = a.notNull | ||
? attributeType.typescriptNamespacedName | ||
: `Nullable<${attributeType.typescriptNamespacedName}>`; | ||
return `${attributeName}${ | ||
a.isOptional ? "?" : "" | ||
}: ${attributeTypeName};`; | ||
}); | ||
return `{${nameAndType.join(" ")}}`; | ||
} | ||
get sqlColumns() { | ||
@@ -95,0 +76,0 @@ return this.attributes.map((a) => a.postgresName).join(","); |
@@ -11,6 +11,6 @@ import { postgresToTypescript } from "./shared"; | ||
const parameters = ``; | ||
const options = `options?: ${node.table.typescriptNamespacedName}.Options`; | ||
const returns = `Promise<${node.table.type.typescriptNamespacedName}[]>`; | ||
// query using postgres driver bindings to the index | ||
const sql = ` | ||
-- | ||
SELECT | ||
@@ -20,8 +20,12 @@ ${node.table.allColumns.map((c) => c.name).join(",")} | ||
${node.table.databaseName} | ||
\${sql.unsafe(\`\${orderBy}\`)} | ||
LIMIT \${options?.limitNumberOfRows ?? Number.MAX_SAFE_INTEGER} | ||
OFFSET \${options?.offsetNumberOfRows ?? 0} | ||
`; | ||
return [ | ||
`async ${node.typescriptPropertyName}(${parameters}) : ${returns}{`, | ||
`async ${node.typescriptPropertyName}(${parameters}${options}) : ${returns}{`, | ||
` | ||
const sql = this.database.context.sql; | ||
const typed = sql.typed as unknown as PostgresTypecasts; | ||
const orderBy = options?.sort ? \`ORDER BY \${options.sort.join(",")}\` : ""; | ||
`, | ||
@@ -28,0 +32,0 @@ `const response = await sql\`${sql}\``, |
@@ -16,4 +16,4 @@ import { postgresToTypescript, sqlPredicate } from "./shared"; | ||
async before(context: GenerationContext, node: ReadOperationNode) { | ||
const generationBuffer = [""]; | ||
const parameters = `${PARAMETERS}: ${node.index.type.typescriptNamespacedName}`; | ||
const options = `options?: ${node.index.table.typescriptNamespacedName}.Options`; | ||
const returns = node.index.unique | ||
@@ -23,12 +23,2 @@ ? `Promise<${node.index.table.type.typescriptNamespacedName}>` | ||
generationBuffer.push( | ||
`async ${node.typescriptPropertyName}(${parameters}) : ${returns}{`, | ||
); | ||
generationBuffer.push( | ||
` | ||
console.assert(parameters); | ||
const sql = this.database.context.sql; | ||
const typed = sql.typed as unknown as PostgresTypecasts; | ||
`, | ||
); | ||
// query using postgres driver bindings to the index | ||
@@ -43,15 +33,23 @@ const sql = ` | ||
${sqlPredicate(context, node.index, PARAMETERS)} | ||
\${sql.unsafe(\`\${orderBy}\`)} | ||
LIMIT \${options?.limitNumberOfRows ?? Number.MAX_SAFE_INTEGER} | ||
OFFSET \${options?.offsetNumberOfRows ?? 0} | ||
`; | ||
generationBuffer.push(`const response = await sql\`${sql}\``); | ||
return [ | ||
`async ${node.typescriptPropertyName}(${parameters}, ${options}) : ${returns}{`, | ||
` | ||
console.assert(parameters); | ||
const sql = this.database.context.sql; | ||
const typed = sql.typed as unknown as PostgresTypecasts; | ||
const orderBy = options?.sort ? \`ORDER BY \${options.sort.join(",")}\` : ""; | ||
`, | ||
`const response = await sql\`${sql}\``, | ||
generationBuffer.push( | ||
`return ${postgresToTypescript(context, node.index.table.type)}${ | ||
node.index.unique ? "[0]" : "" | ||
}`, | ||
); | ||
generationBuffer.push(`}`); | ||
return generationBuffer.join("\n"); | ||
`}`, | ||
].join("\n"); | ||
}, | ||
}; |
@@ -99,4 +99,13 @@ /** | ||
) { | ||
// looking up the operator from the index - default needs to be = | ||
// but can be different for % say in pg_trgm | ||
return node.type.attributes | ||
.map((a) => `${a.name} = ${postgresValueExpression(context, a, holder)}`) | ||
.map( | ||
(a, i) => | ||
`${a.name} ${node.operators[i]} ${postgresValueExpression( | ||
context, | ||
a, | ||
holder, | ||
)}`, | ||
) | ||
.join(" AND "); | ||
@@ -103,0 +112,0 @@ } |
@@ -167,6 +167,8 @@ import { AllOperation } from "./autocrud/all"; | ||
// function call start, passing in parameters | ||
const parameters = node.parametersType | ||
? `parameters : ${node.parametersType?.typescriptNamespacedName}` | ||
: ``; | ||
return [ | ||
await NestedNamedClassVisitor.before(context, node), | ||
`async call(parameters : ${node.parametersType?.typescriptNamespacedName}) {`, | ||
` console.assert(parameters);`, | ||
`async call(${parameters}) {`, | ||
` ${parseResult}`, | ||
@@ -173,0 +175,0 @@ ` const sql = this.database.context.sql;`, |
@@ -23,3 +23,3 @@ import { | ||
node.typescriptNamespacedName | ||
}.call": async (request: EmbraceSQLRequest<object, object>) => database.${ | ||
}.call": async (request: EmbraceSQLRequest<object, object, object>) => database.${ | ||
node.typescriptNamespacedName | ||
@@ -57,3 +57,3 @@ }.call(${callee.join(",")}),`; | ||
` | ||
async dispatch(request: EmbraceSQLRequest<object, object>) { | ||
async dispatch(request: EmbraceSQLRequest<object, object, object>) { | ||
if (!this.dispatchMap[request.operation]) { | ||
@@ -79,3 +79,3 @@ throw new Error(\`\${request.operation} not available\`); | ||
node.typescriptNamespacedPropertyName | ||
}": async (request: EmbraceSQLRequest<object, object>) => database.${ | ||
}": async (request: EmbraceSQLRequest<object, object, object>) => database.${ | ||
node.typescriptNamespacedPropertyName | ||
@@ -87,6 +87,14 @@ }(${callee.join(",")}),`; | ||
before: async (_: GenerationContext, node: AllOperationNode) => { | ||
const callee: string[] = []; | ||
callee.push( | ||
`request.options as ${node.table.typescriptNamespacedName}.Options`, | ||
); | ||
return [ | ||
` | ||
"${node.typescriptNamespacedPropertyName}": async (request: EmbraceSQLRequest<object, object>) => | ||
database.${node.typescriptNamespacedPropertyName}(), | ||
"${ | ||
node.typescriptNamespacedPropertyName | ||
}": async (request: EmbraceSQLRequest<object, object, object>) => | ||
database.${node.typescriptNamespacedPropertyName}(${callee.join( | ||
",", | ||
)}), | ||
`, | ||
@@ -101,6 +109,7 @@ ].join("\n"); | ||
`request.parameters as ${node.index.type.typescriptNamespacedName}`, | ||
`request.options as ${node.index.table.typescriptNamespacedName}.Options`, | ||
); | ||
return `"${ | ||
node.typescriptNamespacedPropertyName | ||
}": async (request: EmbraceSQLRequest<object, object>) => database.${ | ||
}": async (request: EmbraceSQLRequest<object, object, object>) => database.${ | ||
node.typescriptNamespacedPropertyName | ||
@@ -121,3 +130,3 @@ }(${callee.join(",")}),`; | ||
node.typescriptNamespacedPropertyName | ||
}": async (request: EmbraceSQLRequest<object, object>) => database.${ | ||
}": async (request: EmbraceSQLRequest<object, object, object>) => database.${ | ||
node.typescriptNamespacedPropertyName | ||
@@ -135,3 +144,3 @@ }(${callee.join(",")}),`; | ||
node.typescriptNamespacedPropertyName | ||
}": async (request: EmbraceSQLRequest<object, object>) => database.${ | ||
}": async (request: EmbraceSQLRequest<object, object, object>) => database.${ | ||
node.typescriptNamespacedPropertyName | ||
@@ -138,0 +147,0 @@ }(${callee.join(",")}),`; |
@@ -5,2 +5,3 @@ import { GenerationContext } from ".."; | ||
import { generatePrimaryKeyPickers } from "./generatePrimaryKeyPickers"; | ||
import { generateTypeComparison } from "./generateTypeComparison"; | ||
import { generateTypeGuards } from "./generateTypeGuards"; | ||
@@ -11,5 +12,8 @@ import { generateTypeParsers } from "./generateTypeParsers"; | ||
AbstractTypeNode, | ||
ColumnNode, | ||
CompositeTypeNode, | ||
NamespaceVisitor, | ||
VALUES, | ||
cleanIdentifierForTypescript, | ||
isNodeType, | ||
} from "@embracesql/shared"; | ||
@@ -20,2 +24,57 @@ import { GenerationContext as GC } from "@embracesql/shared"; | ||
/** | ||
* All available columns -- generate the sort option permumtations | ||
* as an enum. | ||
*/ | ||
function sortOptions(columns: ColumnNode[]) { | ||
return [ | ||
`enum SortOptions {`, | ||
`${columns | ||
.map((c) => `${c.typescriptPropertyName}Ascending = "${c.name} ASC",`) | ||
.join("\n")}`, | ||
`${columns | ||
.map((c) => `${c.typescriptPropertyName}Descending = "${c.name} DESC",`) | ||
.join("\n")}`, | ||
`}`, | ||
].join("\n"); | ||
} | ||
/** | ||
* All available columns -- generate column metadata. | ||
*/ | ||
async function columnMetadata(context: GC, type: CompositeTypeNode) { | ||
return await type.visit({ | ||
...context, | ||
handlers: { | ||
[ASTKind.CompositeType]: { | ||
before: async () => `export const Columns = {`, | ||
after: async (_, node) => { | ||
return [ | ||
`}`, // close columns | ||
// convenience iterable column names | ||
`export const ColumnNames = [${node.attributes | ||
.map((c) => `"${c.typescriptName}"`) | ||
.join(",")}] as const;`, | ||
`export const FieldNames = [${node.attributes | ||
.map((c) => `"${c.typescriptPropertyName}"`) | ||
.join(",")}] as const;`, | ||
`type FieldNamesType = typeof FieldNames[number];`, | ||
].join("\n"); | ||
}, | ||
}, | ||
[ASTKind.Attribute]: { | ||
before: async (_, node) => { | ||
// each column worth of metadata including a type constant | ||
return [ | ||
`${node.typescriptName}: {`, | ||
` typeName: "${node.type.typescriptNamespacedName}",`, | ||
` fieldName: "${node.typescriptPropertyName}" as FieldNamesType,`, | ||
`},`, | ||
].join("\n"); | ||
}, | ||
}, | ||
}, | ||
}); | ||
} | ||
/** | ||
* Generate TypeScript type definitions for all types available | ||
@@ -44,3 +103,3 @@ * in the database schema catalog along with request/response message | ||
import {UUID, JsDate, JSONValue, JSONObject, Empty, Nullable, NullableMembers, undefinedIsNull, nullIsUndefined, NEVER} from "@embracesql/shared"; | ||
import type { PartiallyOptional } from "@embracesql/shared"; | ||
import type { PartiallyOptional, PossiblyEmpty, ReadOptions, Sort } from "@embracesql/shared"; | ||
@@ -68,2 +127,9 @@ `, | ||
handlers: { | ||
[ASTKind.Database]: { | ||
before: async () => { | ||
return [ | ||
// schema wide shared | ||
].join("\n"); | ||
}, | ||
}, | ||
[ASTKind.Schema]: NamespaceVisitor, | ||
@@ -93,9 +159,5 @@ [ASTKind.Types]: NamespaceVisitor, | ||
// empty placeholder row used in UI adding | ||
`export function emptyRow() {`, | ||
`export function emptyRow() : PossiblyEmpty<${node.type.typescriptNamespacedName}> {`, | ||
` return ${emptyTypescriptRow(context, node.type)};`, | ||
`}`, | ||
].join("\n"); | ||
}, | ||
after: async (context, node) => { | ||
return [ | ||
// exhaustive -- if there is no primary key, say so explicitly | ||
@@ -116,5 +178,12 @@ node.primaryKey ? "" : `export type PrimaryKey = never;`, | ||
}, Optional & PrimaryKey>`, | ||
await NamespaceVisitor.after(context, node), | ||
// read options exist per table | ||
`export ${sortOptions(node.allColumns)};`, | ||
`export type Options = ReadOptions & {`, | ||
` sort?: SortOptions[],`, | ||
`};`, | ||
// a convenient metadata constant for all columns | ||
await columnMetadata(context, node.type), | ||
].join("\n"); | ||
}, | ||
after: NamespaceVisitor.after, | ||
}, | ||
@@ -131,3 +200,28 @@ [ASTKind.Index]: { | ||
[ASTKind.Procedures]: NamespaceVisitor, | ||
[ASTKind.Procedure]: NamespaceVisitor, | ||
[ASTKind.Procedure]: { | ||
before: async (context, node) => { | ||
return [ | ||
await NamespaceVisitor.before(context, node), | ||
isNodeType(node.resultsType, ASTKind.CompositeType) | ||
? await columnMetadata(context, node.resultsType) | ||
: "", | ||
].join("\n"); | ||
}, | ||
after: NamespaceVisitor.after, | ||
}, | ||
[ASTKind.DomainType]: TypeDefiner, | ||
[ASTKind.ArrayType]: TypeDefiner, | ||
[ASTKind.Scripts]: NamespaceVisitor, | ||
[ASTKind.ScriptFolder]: NamespaceVisitor, | ||
[ASTKind.Script]: { | ||
before: async (context, node) => { | ||
return [ | ||
await NamespaceVisitor.before(context, node), | ||
isNodeType(node.resultsType, ASTKind.CompositeType) | ||
? await columnMetadata(context, node.resultsType) | ||
: "", | ||
].join("\n"); | ||
}, | ||
after: NamespaceVisitor.after, | ||
}, | ||
[ASTKind.CompositeType]: { | ||
@@ -143,7 +237,2 @@ // composite types are a name and AttributeNode(s) will fill the body | ||
}, | ||
[ASTKind.DomainType]: TypeDefiner, | ||
[ASTKind.ArrayType]: TypeDefiner, | ||
[ASTKind.Scripts]: NamespaceVisitor, | ||
[ASTKind.ScriptFolder]: NamespaceVisitor, | ||
[ASTKind.Script]: NamespaceVisitor, | ||
[ASTKind.Attribute]: { | ||
@@ -170,4 +259,5 @@ before: async (_, node) => { | ||
generationBuffer.push(await generateTypeGuards(context)); | ||
generationBuffer.push(await generateTypeComparison(context)); | ||
return generationBuffer.join("\n"); | ||
}; |
@@ -20,7 +20,2 @@ import { | ||
handlers: { | ||
[ASTKind.Database]: { | ||
before: async () => { | ||
return `// begin type guards`; | ||
}, | ||
}, | ||
[ASTKind.Schema]: NamespaceVisitor, | ||
@@ -27,0 +22,0 @@ [ASTKind.Tables]: NamespaceVisitor, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
123929
59
3691
+ Added@embracesql/shared@0.1.3(transitive)
- Removed@embracesql/shared@0.0.14(transitive)
Updated@embracesql/shared@^0.1.0