Skip to content
Merged
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
131 changes: 1 addition & 130 deletions scripts/db-pull-dump.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Pool } from "pg";
import { writeFileSync, mkdirSync, createWriteStream } from "fs";
import { mkdirSync, createWriteStream } from "fs";
import { resolve } from "path";
import { config } from "dotenv";

Expand Down Expand Up @@ -30,135 +30,6 @@ async function main() {
}
}

async function generateSchemaSql(pool: Pool): Promise<string> {
const schemas = await pool.query(`
SELECT schema_name FROM information_schema.schemata
WHERE schema_name NOT IN ('information_schema', 'pg_catalog', 'pg_toast', 'drizzle', 'auth', 'temp')
ORDER BY schema_name
`);

const lines: string[] = [
"-- Schema dump from GCP",
`-- Generated at ${new Date().toISOString()}`,
"",
];

for (const s of schemas.rows) {
if (s.schema_name !== "public") {
lines.push(`CREATE SCHEMA IF NOT EXISTS ${s.schema_name};`);
}
}
lines.push("");

const tables = await pool.query(`
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast', 'drizzle', 'auth', 'temp')
AND table_type = 'BASE TABLE'
ORDER BY table_schema, table_name
`);

for (const table of tables.rows) {
const fullName = `"${table.table_schema}"."${table.table_name}"`;
const cols = await pool.query(
`
SELECT column_name, data_type, udt_name, is_nullable, column_default,
character_maximum_length, numeric_precision, numeric_scale,
is_identity, identity_generation
FROM information_schema.columns
WHERE table_schema = $1 AND table_name = $2
ORDER BY ordinal_position
`,
[table.table_schema, table.table_name],
);

lines.push(`CREATE TABLE IF NOT EXISTS ${fullName} (`);
const colDefs: string[] = [];
for (const col of cols.rows) {
const typeName = getColumnType(col);
let def = ` "${col.column_name}" ${typeName}`;
if (col.column_default && col.is_identity !== "YES") {
def += ` DEFAULT ${col.column_default}`;
}
if (col.is_identity === "YES") {
def += ` GENERATED ${col.identity_generation === "ALWAYS" ? "ALWAYS" : "BY DEFAULT"} AS IDENTITY`;
}
if (col.is_nullable === "NO") {
def += " NOT NULL";
}
colDefs.push(def);
}

const pk = await pool.query(
`
SELECT kcu.column_name
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
AND tc.table_schema = kcu.table_schema
WHERE tc.table_schema = $1 AND tc.table_name = $2
AND tc.constraint_type = 'PRIMARY KEY'
ORDER BY kcu.ordinal_position
`,
[table.table_schema, table.table_name],
);

if (pk.rows.length > 0) {
const pkCols = pk.rows.map((r) => `"${r.column_name}"`).join(", ");
colDefs.push(` PRIMARY KEY (${pkCols})`);
}

lines.push(colDefs.join(",\n"));
lines.push(");");
lines.push("");
}

const indexes = await pool.query(`
SELECT schemaname, tablename, indexname, indexdef
FROM pg_indexes
WHERE schemaname NOT IN ('information_schema', 'pg_catalog', 'pg_toast', 'drizzle', 'auth', 'temp')
AND indexdef NOT LIKE '%PRIMARY%'
AND indexname NOT LIKE '%_pkey'
ORDER BY schemaname, tablename, indexname
`);

if (indexes.rows.length > 0) {
lines.push("-- Indexes");
for (const idx of indexes.rows) {
lines.push(`${idx.indexdef};`);
}
lines.push("");
}

return lines.join("\n");
}

function getColumnType(col: Record<string, unknown>): string {
const udtName = col.udt_name as string;
const dataType = col.data_type as string;
const maxLen = col.character_maximum_length as number | null;
const numPrecision = col.numeric_precision as number | null;
const numScale = col.numeric_scale as number | null;

switch (dataType) {
case "character varying":
return maxLen ? `VARCHAR(${maxLen})` : "VARCHAR";
case "character":
return maxLen ? `CHAR(${maxLen})` : "CHAR";
case "numeric":
if (numPrecision && numScale)
return `NUMERIC(${numPrecision}, ${numScale})`;
if (numPrecision) return `NUMERIC(${numPrecision})`;
return "NUMERIC";
case "ARRAY":
return `${udtName.replace(/^_/, "")}[]`;
case "USER-DEFINED":
return udtName;
default:
return dataType;
}
}

// Escape a value for COPY tab-delimited format
function escapeCopyValue(val: unknown): string {
if (val === null || val === undefined) return "\\N";
Expand Down
15 changes: 15 additions & 0 deletions scripts/firebase-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ load_environment_variables() {
current_value="$value_part"
in_multiline=true
fi
elif [[ "$value_part" =~ ^\' ]]; then
value_part="${value_part#\'}"

if [[ "$value_part" =~ \'$ ]]; then
current_value="${value_part%\'}"
export "$current_var=$current_value"
else
current_value="$value_part"
in_multiline=true
fi
else
export "$current_var=$value_part"
fi
Expand All @@ -202,6 +212,11 @@ load_environment_variables() {
${line%\"}"
export "$current_var=$current_value"
in_multiline=false
elif [[ "$line" =~ \'$ ]]; then
current_value="$current_value
${line%\'}"
export "$current_var=$current_value"
in_multiline=false
else
current_value="$current_value
$line"
Expand Down