mirror of
https://github.com/supabase/supabase.git
synced 2026-05-06 22:18:00 +08:00
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
import { SupaTable } from '@/components/grid/types'
|
|
import { Lint } from '@/data/lint/lint-query'
|
|
|
|
export const getEntityLintDetails = (
|
|
entityName: string,
|
|
lintName: string,
|
|
lintLevels: ('ERROR' | 'WARN' | 'INFO')[],
|
|
lints: Lint[],
|
|
schema: string
|
|
): { hasLint: boolean; count: number; matchingLint: Lint | null } => {
|
|
const matchingLint =
|
|
lints?.find(
|
|
(lint) =>
|
|
lint?.metadata?.name === entityName &&
|
|
lint?.metadata?.schema === schema &&
|
|
lint?.name === lintName &&
|
|
lintLevels.includes(lint?.level)
|
|
) || null
|
|
|
|
return {
|
|
hasLint: matchingLint !== null,
|
|
count: matchingLint ? 1 : 0,
|
|
matchingLint,
|
|
}
|
|
}
|
|
|
|
export const formatTableRowsToSQL = (table: SupaTable, rows: any[]) => {
|
|
if (rows.length === 0) return ''
|
|
|
|
const columns = table.columns.map((col) => `"${col.name}"`).join(', ')
|
|
|
|
const valuesSets = rows
|
|
.map((row) => {
|
|
const filteredRow = { ...row }
|
|
if ('idx' in filteredRow) delete filteredRow.idx
|
|
|
|
const values = Object.entries(filteredRow).map(([key, val]) => {
|
|
const { dataType, format } = table.columns.find((col) => col.name === key) ?? {}
|
|
|
|
// We only check for NULL, array and JSON types, everything else we stringify
|
|
// given that Postgres can implicitly cast the right type based on the column type
|
|
// For string types, we need to deal with escaping single quotes
|
|
const stringFormats = ['text', 'varchar']
|
|
|
|
if (val === null) {
|
|
return 'null'
|
|
} else if (dataType === 'ARRAY') {
|
|
const array = Array.isArray(val) ? val : JSON.parse(val as string)
|
|
return `${formatArrayForSql(array as unknown[])}`
|
|
} else if (format?.includes('json')) {
|
|
return `${JSON.stringify(val).replace(/\\"/g, '"').replace(/'/g, "''").replace('"', "'").replace(/.$/, "'")}`
|
|
} else if (
|
|
typeof format === 'string' &&
|
|
typeof val === 'string' &&
|
|
stringFormats.includes(format)
|
|
) {
|
|
return `'${val.replaceAll("'", "''")}'`
|
|
} else if (typeof val === 'number' || typeof val === 'boolean') {
|
|
return `${val}`
|
|
} else if (typeof val === 'string') {
|
|
return `'${val.replaceAll("'", "''")}'`
|
|
} else {
|
|
return `'${val}'`
|
|
}
|
|
})
|
|
|
|
return `(${values.join(', ')})`
|
|
})
|
|
.join(', ')
|
|
|
|
return `INSERT INTO "${table.schema}"."${table.name}" (${columns}) VALUES ${valuesSets};`
|
|
}
|
|
|
|
/**
|
|
* Generate a random tag for dollar-quoting of SQL strings
|
|
*
|
|
* @return A random tag in the format `$tag$`
|
|
*/
|
|
const generateRandomTag = (): `$${string}$` => {
|
|
const inner = Math.random().toString(36).substring(2, 15)
|
|
// Ensure the tag starts with a character not a digit to avoid conflicts with
|
|
// Postgres parameter syntax
|
|
return `$x${inner}$`
|
|
}
|
|
|
|
/**
|
|
* Wrap a string in dollar-quote tags, ensuring the tag does not appear in the string
|
|
*
|
|
* @throws Error if unable to generate a unique dollar-quote tag after multiple attempts
|
|
*/
|
|
const safeDollarQuote = (str: string): string => {
|
|
let tag = generateRandomTag()
|
|
|
|
let attempts = 0
|
|
const maxAttempts = 100
|
|
while (str.includes(tag)) {
|
|
if (attempts >= maxAttempts) {
|
|
throw new Error('Unable to generate a unique dollar-quote tag after multiple attempts.')
|
|
}
|
|
|
|
attempts++
|
|
tag = generateRandomTag()
|
|
}
|
|
return `${tag}${str}${tag}`
|
|
}
|
|
|
|
const formatArrayForSql = (arr: unknown[]): string => {
|
|
let result = 'ARRAY['
|
|
|
|
arr.forEach((item, index) => {
|
|
if (Array.isArray(item)) {
|
|
result += formatArrayForSql(item)
|
|
} else if (typeof item === 'string') {
|
|
result += `'${item.replaceAll("'", "''")}'`
|
|
} else if (!!item && typeof item === 'object') {
|
|
result += `${safeDollarQuote(JSON.stringify(item))}::json`
|
|
} else {
|
|
result += `${item}`
|
|
}
|
|
|
|
if (index < arr.length - 1) {
|
|
result += ','
|
|
}
|
|
})
|
|
|
|
result += ']'
|
|
|
|
return result
|
|
}
|