liam icon indicating copy to clipboard operation
liam copied to clipboard

(not merge) ✨add: PGlitePage

Open NoritakaIkeda opened this issue 7 months ago • 3 comments

Issue

  • resolve:

Why is this change needed?

What would you like reviewers to focus on?

Testing Verification

What was done

pr_agent:summary

Detailed Changes

pr_agent:walkthrough

Additional Notes

NoritakaIkeda avatar May 29 '25 08:05 NoritakaIkeda

⚠️ No Changeset found

Latest commit: f056025dfc170174d98ae36dae159ac11b69deb5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

changeset-bot[bot] avatar May 29 '25 08:05 changeset-bot[bot]

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
liam-app ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 5, 2025 5:40am
liam-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 5, 2025 5:40am
liam-erd-sample ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 5, 2025 5:40am
liam-storybook ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 5, 2025 5:40am

vercel[bot] avatar May 29 '25 08:05 vercel[bot]

Updates to Preview Branch (feature/pglite) ↗︎

Deployments Status Updated
Database Thu, 05 Jun 2025 05:37:11 UTC
Services Thu, 05 Jun 2025 05:37:11 UTC
APIs Thu, 05 Jun 2025 05:37:11 UTC

Tasks are run on every commit but only new migration files are pushed. Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Thu, 05 Jun 2025 05:37:11 UTC
Migrations Thu, 05 Jun 2025 05:37:11 UTC
Seeding Thu, 05 Jun 2025 05:37:11 UTC
Edge Functions Thu, 05 Jun 2025 05:37:11 UTC

View logs for this Workflow Run ↗︎. Learn more about Supabase for Git ↗︎.

supabase[bot] avatar Jun 05 '25 02:06 supabase[bot]

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 Security concerns

SQL injection:
Both applyDDL.ts and applyDML.ts directly execute SQL statements from user input without proper parameterization or sanitization. The code splits input by semicolons and executes each statement, but doesn't use prepared statements or parameter binding. This could allow malicious users to inject harmful SQL commands if the playground is used with untrusted input sources, especially when controlled by LLM agents as mentioned in the PR description.

⚡ Recommended focus areas for review

SQL Injection Risk

The code directly executes SQL statements from user input without sanitization or parameterization, which could allow for SQL injection attacks if used with untrusted input.

const statements = ddlText
  .split(';')
  .map((s) => s.trim())
  .filter(Boolean)

// Execute each SQL statement sequentially
for (const sql of statements) {
  const startTime = performance.now()
  try {
    const result = await db.query(sql)
Race Condition

The addDMLWithQuery method creates a section and then immediately tries to execute a query on it, but there's a potential race condition since the section might not be fully initialized in state before the query execution.

// Add new DML section with query and execute
addDMLWithQuery: async (query: string) => {
  if (!globalDb) return

  const newDb = new PGlite()

  // Apply current DDL to the new DB instance
  if (ddlState.ddlInput) {
    await applyDDL(ddlState.ddlInput, newDb)
  }

  const newSectionId = crypto.randomUUID()

  // Add new section
  setDmlSections((prev) => [
    ...prev,
    {
      id: newSectionId,
      dmlInput: query,
      results: [],
      db: newDb,
    },
  ])

  // Execute the query in the new section
  const results = await applyDML(query, newDb)

  setDmlSections((prev) => {
    const sectionIndex = prev.findIndex((s) => s.id === newSectionId)
    if (sectionIndex === -1) return prev

    const newSections = [...prev]
    newSections[sectionIndex] = {
      ...newSections[sectionIndex],
      results,
      dmlInput: '', // Clear input after execution
    }
    return newSections
  })
},
Error Handling

The error handling captures all errors generically but doesn't provide specific handling for different types of SQL errors, which could make debugging difficult for users.

} catch (error) {
  const executionTime = Math.round(performance.now() - startTime)
  const errorMessage =
    error instanceof Error ? error.message : String(error)
  results.push({
    sql,
    result: { error: errorMessage },
    success: false,
    id: crypto.randomUUID(),
    metadata: {
      executionTime,
      timestamp: new Date().toLocaleString(),
    },
  })

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Improve SQL statement parsing

The current SQL statement splitting logic doesn't handle SQL comments or string
literals containing semicolons correctly. This could cause SQL statements to be
incorrectly parsed and executed. Consider using a more robust SQL parser or
regex pattern to handle these edge cases.

frontend/apps/app/components/PGlitePage/utils/applyDDL.ts [17-20]

+// Simple regex to avoid splitting on semicolons within quotes or comments
 const statements = ddlText
+  .replace(/--.*?(\r?\n|$)/g, '$1') // Remove single line comments
+  .replace(/'(?:[^']|'')*'/g, match => match.replace(/;/g, '\u0000')) // Replace ; in strings with placeholder
   .split(';')
-  .map((s) => s.trim())
+  .map(s => s.trim().replace(/\u0000/g, ';')) // Restore ; in strings
   .filter(Boolean)
  • [ ] Apply / Chat <!-- /improve --apply_suggestion=0 -->
Suggestion importance[1-10]: 7

__

Why: Valid suggestion addressing a potential bug where SQL statements containing semicolons in comments or string literals could be incorrectly parsed. This could lead to malformed SQL execution and errors.

Medium
Learned
best practice
Prevent updates on unmounted components

Add a cleanup function to the useEffect hook to prevent state updates if the
component unmounts during async operations. This prevents memory leaks and React
warnings about updating state on unmounted components.

frontend/apps/app/components/PGlitePage/PGlitePlayground.tsx [129-139]

 useEffect(() => {
+  let isMounted = true;
   const initializeDb = async () => {
     const db = new PGlite()
-    setGlobalDb(db)
-
-    // Add one initial DML section
-    addDMLSection(db)
+    if (isMounted) {
+      setGlobalDb(db)
+      // Add one initial DML section
+      addDMLSection(db)
+    }
   }
 
   initializeDb()
+  return () => {
+    isMounted = false;
+  };
 }, [addDMLSection])
  • [ ] Apply / Chat <!-- /improve --apply_suggestion=1 -->
Suggestion importance[1-10]: 6

__

Why: Relevant best practice - Prevent state updates on unmounted components by using cleanup functions in useEffect hooks to avoid memory leaks and React warnings.

Low
General
Preserve user input after execution

The executeDML function clears the input field after execution, which may
confuse users who want to review or modify their query after seeing the results.
Consider keeping the input and adding a separate clear button instead of
automatically clearing the input.

frontend/apps/app/components/PGlitePage/PGlitePlayground.tsx [203-223]

 // Execute DML (for a specific section)
 const executeDML = async (sectionId: string) => {
   const sectionIndex = dmlSections.findIndex((s) => s.id === sectionId)
   if (sectionIndex === -1) return
 
   const section = dmlSections[sectionIndex]
   if (!section.db || !section.dmlInput.trim()) return
 
   // Execute DML and save results
   const results = await applyDML(section.dmlInput, section.db)
 
   setDmlSections((prev) => {
     const newSections = [...prev]
     newSections[sectionIndex] = {
       ...newSections[sectionIndex],
       results: [...newSections[sectionIndex].results, ...results],
-      dmlInput: '', // Clear input
+      // Keep the input to allow users to modify and rerun queries
     }
     return newSections
   })
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: Reasonable UX improvement suggestion to preserve dmlInput after execution, allowing users to review and modify queries. This is a subjective design choice rather than a critical issue.

Low
  • [ ] Update <!-- /improve_multi --more_suggestions=true -->

I'm planning to resolve only the conflicts in the lock file and then proceed with the merge.

NoritakaIkeda avatar Jun 05 '25 05:06 NoritakaIkeda

Thank you again!

MH4GF avatar Jun 05 '25 06:06 MH4GF