Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,20 @@ Generate PLV8 functions for an input typescript file
| --scope-prefix | String | Specify a scope prefix, by default `plv8ify`, adds `plv8ify_` as prefix for exported typescript functions | `plv8ify` |
| --pg-function-delimiter | String | Specify a delimiter for the generated Postgres function | `$plv8ify$` |
| --fallback-type | String | Specify a fallback type when `plv8ify` fails to map a detected Typescript type to a Postges type | `JSONB` |
| --mode | 'inline', 'bundle' or 'start_proc' | 'inline' will bundle the library in each function, both 'bundle' and 'start_proc' creates a `{prefix}_init` function that loads the library. 'bundle' adds a check to each function to call 'init' if required, whereas 'start_proc' is designed to be used with plv8.start_proc | `inline` |
| --mode | 'inline' or 'bundle' or 'start_proc' | 'inline' will bundle the library in each function, both 'bundle' and 'start_proc' creates a `{prefix}_init` function that loads the library. 'bundle' adds a check to each function to call 'init' if required, whereas 'start_proc' is designed to be used with plv8.start_proc | `inline` |
| --volatility | 'IMMUTABLE' or 'STABLE' or 'VOLATILE' | Change the volatility of all the generated functions. To change volatility of a specific function use the comment format `//@plv8ify-volatility-STABLE` in the input typescript file (see `examples/turf-js/input.ts`). Note that for now only single-line comment syntax is supported. | `IMMUTABLE` |

### Deploy

Deploy an output folder to a Postgres database (defined by env var `DATABASE_URL`)

| Generate Command Flags | Type | Description | Default |
| ---------------------- | ------ | ------------------------ | -------------- |
| --output-folder | String | Specify an output folder | `plv8ify-dist` |
| Generate Command Flags | Type | Description | Default |
| ------------------------------ | ----------------------- | ------------------------------------------------------ | ---------------------------------------------- |
| --output-folder | String | Specify an output folder | `plv8ify-dist` |
| --deploy-mode | 'functions' or 'pg_tle' | Choose to deploy as functions or as a pg_tle extension | `functions` |
| --pg-tle-extension-name | String | pg_tle extension name | `plv8ify_pg_tle` |
| --pg-tle-extension-version | String | pg_tle extension version | `0.1` |
| --pg-tle-extension-description | String | pg_tle extension description | `plv8ify_pg_tle <...some default description>` |

## Caveats

Expand Down
119 changes: 86 additions & 33 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import dotenv from 'dotenv'
import fs from 'fs'
import path from 'path'
import task from 'tasuku'
import { match } from 'ts-pattern'

import { Database } from '../helpers/Database'
import { ParseCLI } from '../helpers/ParseCLI'
Expand Down Expand Up @@ -64,43 +65,95 @@ export async function deployCommand(
}

const db = database.getConnection()
let deployCommands = fs
.readdirSync(outputFolderPath)
// Only extract .plv8.sql files, this will need to change if we ever make the extension configurable
.filter((file) => file.endsWith('.plv8.sql'))
.map((file) => {
const filePath = path.join(outputFolderPath, file)
return {
filePath,
sqlQueryPromise: db.file(filePath),
}
})

await task(
`Deploying files from ${outputFolderPath} to the provided PostgreSQL database 🚧`.trim(),
async ({ setWarning }) => {
const taskGroup = await task.group((task) =>
deployCommands.map((deployCommand) => {
const name = getFunctionNameFromFilePath(deployCommand.filePath)
return task(
`Deploying ${name}`,
async ({ setTitle: _setTitle, setError: _setError }) => {
try {
await deployCommand.sqlQueryPromise
_setTitle(`Deployed ${name}`)
} catch (e) {
_setError(`Failed to deploy ${name} (because of ${e.message})`)
setWarning(`Failed to some functions (see below))`)
}
}
await match(CLI.config.deployMode)
.with('functions', async () => {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added two modes, 'functions' mode gets the existing code that was there.

const deployCommands = fs
.readdirSync(outputFolderPath)
// Only extract .plv8.sql files, this will need to change if we ever make the extension configurable
.filter((file) => file.endsWith('.plv8.sql'))
.map((file) => {
const filePath = path.join(outputFolderPath, file)
return {
filePath,
sqlQueryPromise: db.file(filePath),
}
})

await task(
`Deploying files from ${outputFolderPath} to the provided PostgreSQL database 🚧`.trim(),
async ({ setWarning }) => {
const taskGroup = await task.group((task) =>
deployCommands.map((deployCommand) => {
const name = getFunctionNameFromFilePath(deployCommand.filePath)
return task(
`Deploying ${name}`,
async ({ setTitle: _setTitle, setError: _setError }) => {
try {
await deployCommand.sqlQueryPromise
_setTitle(`Deployed ${name}`)
} catch (e) {
_setError(
`Failed to deploy ${name} (because of ${e.message})`
)
setWarning(`Failed to some functions (see below))`)
}
}
)
})
)

// TODO: add some batching here
await Promise.allSettled(taskGroup)
}
)
})
.with('pg_tle', async () => {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added two modes, 'pg_tle' mode gets the new code that instead of executing each individual SQL function file, collects them as a single string and executes SELECT pgtle.install_extension with appropriate parameters on it.

const deployableFunctionsSQL = fs
.readdirSync(outputFolderPath)
// Only extract .plv8.sql files, this will need to change if we ever make the extension configurable
.filter((file) => file.endsWith('.plv8.sql'))
.map((file) => {
const filePath = path.join(outputFolderPath, file)
const fileContents = fs.readFileSync(filePath, 'utf-8')
return fileContents
})
.join('\n')
await task(
`Deploying files from ${outputFolderPath} to the provided PostgreSQL database as a pg_tle extension 🚧`.trim(),
async ({ setTitle, setError }) => {
try {
const extensionName = CLI.config.pgTLEExtensionName
const extensionVersion = CLI.config.pgTLEExtensionVersion
const extensionDescription = CLI.config.pgTLEExtensionDescription
await db.begin(async (db) => {
await db`CREATE EXTENSION IF NOT EXISTS pg_tle`
await db`DROP EXTENSION IF EXISTS ${db(extensionName)}`
await db`SELECT pgtle.uninstall_extension_if_exists(${extensionName})`
await db`SELECT pgtle.install_extension(
${extensionName},
${extensionVersion},
${extensionDescription},
${deployableFunctionsSQL}
)`
await db`CREATE EXTENSION IF NOT EXISTS ${db(extensionName)}`
})
await db.listen('notices', (payload) => {
console.log({ payload })
})
setTitle(
`Deployed files from ${outputFolderPath} to the provided PostgreSQL database as a pg_tle extension ✅`
)
} catch (e) {
setError(
'Failed to deploy the functions in the provided output folder as a pg_tle extension because: ' +
e
)
}
}
)

// TODO: add some batching here
await Promise.allSettled(taskGroup)
}
)
})
.exhaustive()

database.endConnection()
}
17 changes: 17 additions & 0 deletions src/helpers/ParseCLI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import arg from 'arg'

type Command = 'version' | 'generate' | 'deploy'
type DeployMode = 'functions' | 'pg_tle'

export class ParseCLI {
static getCommand() {
Expand All @@ -16,6 +17,11 @@ export class ParseCLI {
'--mode': String,
'--volatility': String,
'--debug': Boolean,

'--deploy-mode': String,
'--pg-tle-extension-name': String,
'--pg-tle-extension-version': String,
'--pg-tle-extension-description': String,
})

if (args._.length === 0) {
Expand All @@ -35,6 +41,13 @@ Please specify a command. Available commands: generate, version, deploy
const mode = (args['--mode'] || 'inline') as Mode
const defaultVolatility = (args['--volatility'] ||
'IMMUTABLE') as Volatility
const deployMode = (args['--deploy-mode'] || 'functions') as DeployMode
const pgTLEExtensionName = (args['--pg-tle-extension-name'] ||
'plv8ify_pg_tle') as string
const pgTLEExtensionVersion = (args['--pg-tle-extension-version'] ||
'0.1') as string
const pgTLEExtensionDescription = (args['--pg-tle-extension-description'] ||
'plv8ify_pg_tle extension is the default name of extensions deployed with plv8ify, please pass --pg-tle-extension-name as a CLI argument to override this') as string

return {
command: args._[0] as Command,
Expand All @@ -47,6 +60,10 @@ Please specify a command. Available commands: generate, version, deploy
fallbackReturnType,
mode,
defaultVolatility,
deployMode,
pgTLEExtensionName,
pgTLEExtensionVersion,
pgTLEExtensionDescription,
},
}
}
Expand Down