main.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. package main
  2. import (
  3. "bufio"
  4. "context"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "os"
  10. "os/signal"
  11. "path/filepath"
  12. "strings"
  13. "syscall"
  14. "time"
  15. "github.com/chzyer/readline"
  16. "github.com/danfragoso/pizzasql-next/pkg/analyzer"
  17. "github.com/danfragoso/pizzasql-next/pkg/csvexport"
  18. "github.com/danfragoso/pizzasql-next/pkg/csvimport"
  19. "github.com/danfragoso/pizzasql-next/pkg/executor"
  20. "github.com/danfragoso/pizzasql-next/pkg/httpserver"
  21. "github.com/danfragoso/pizzasql-next/pkg/kvmanager"
  22. "github.com/danfragoso/pizzasql-next/pkg/lexer"
  23. "github.com/danfragoso/pizzasql-next/pkg/parser"
  24. "github.com/danfragoso/pizzasql-next/pkg/pgserver"
  25. pizzaruntime "github.com/danfragoso/pizzasql-next/pkg/runtime"
  26. "github.com/danfragoso/pizzasql-next/pkg/sqlexport"
  27. "github.com/danfragoso/pizzasql-next/pkg/sqlimport"
  28. "github.com/danfragoso/pizzasql-next/pkg/sqliteimport"
  29. "github.com/danfragoso/pizzasql-next/pkg/storage"
  30. "github.com/danfragoso/pizzasql-next/pkg/version"
  31. )
  32. var (
  33. kvAddr = flag.String("kvaddr", "", "PizzaKV server address (default: auto-connect to managed instance)")
  34. kvLaunch = flag.Bool("kv", false, "Launch PizzaKV automatically")
  35. kvFlags = flag.String("kvflags", "", "Flags to pass to PizzaKV (e.g., \"-iwal\")")
  36. forceYes = flag.Bool("y", false, "Auto-accept prompts (skip interactive confirmation)")
  37. database = flag.String("db", "pizzasql", "Database name")
  38. poolSize = flag.Int("pool", 100, "Connection pool size")
  39. timeout = flag.Duration("timeout", 120*time.Second, "Query timeout")
  40. httpEnable = flag.Bool("http", false, "Enable HTTP server")
  41. httpHost = flag.String("http-host", "localhost", "HTTP server host")
  42. httpPort = flag.Int("http-port", 8080, "HTTP server port")
  43. httpCORS = flag.Bool("http-cors", true, "Enable CORS")
  44. httpAuth = flag.Bool("http-auth", false, "Enable authentication")
  45. httpCompression = flag.Bool("http-compression", true, "Enable HTTP response compression")
  46. quiet = flag.Bool("quiet", false, "Disable request/query logging")
  47. apiKeys = flag.String("api-keys", "", "Comma-separated API keys")
  48. // PostgreSQL wire protocol server flags
  49. pgEnable = flag.Bool("pg", false, "Enable PostgreSQL wire protocol server")
  50. pgHost = flag.String("pg-host", "localhost", "PostgreSQL server host")
  51. pgPort = flag.Int("pg-port", 5432, "PostgreSQL server port")
  52. // Export/Import flags
  53. exportFile = flag.String("o", "", "Output file for export")
  54. importFile = flag.String("i", "", "Input file for import")
  55. exportTable = flag.String("table", "", "Specific table to export (empty = all)")
  56. exportDrop = flag.Bool("drop", false, "Include DROP TABLE statements in export")
  57. ignoreErrors = flag.Bool("ignore-errors", false, "Continue import on errors")
  58. exportFormat = flag.String("format", "", "Export/import format: sql, csv (auto-detect from extension)")
  59. createTable = flag.Bool("create-table", false, "Create table if not exists (CSV import)")
  60. )
  61. var kvManager *kvmanager.Manager
  62. var startPprofServerHook func() *http.Server
  63. func main() {
  64. flag.Parse()
  65. // Warn if other pizzasql instances are running; prompt to continue.
  66. if err := pizzaruntime.CheckExistingInstances(*forceYes); err != nil {
  67. fmt.Fprintf(os.Stderr, "%v\n", err)
  68. os.Exit(1)
  69. }
  70. // Register this process in its own runtime directory.
  71. pizzaruntime.WritePizzaSQL(os.Getpid(), 0, 0)
  72. defer pizzaruntime.Cleanup()
  73. // Set up signal handling for graceful shutdown.
  74. sigChan := make(chan os.Signal, 1)
  75. signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
  76. go func() {
  77. <-sigChan
  78. fmt.Println("\nShutting down...")
  79. stopPizzaKV()
  80. pizzaruntime.Cleanup()
  81. os.Exit(0)
  82. }()
  83. // If -kv flag is set, always launch a dedicated PizzaKV for this instance.
  84. if *kvLaunch {
  85. if err := launchPizzaKV(); err != nil {
  86. fmt.Fprintf(os.Stderr, "Failed to launch PizzaKV: %v\n", err)
  87. os.Exit(1)
  88. }
  89. defer stopPizzaKV()
  90. } else if *kvAddr == "" {
  91. *kvAddr = "localhost:8085"
  92. }
  93. // Start whichever servers are enabled, then block until signal.
  94. if *httpEnable || *pgEnable {
  95. httpRuntimePort := 0
  96. pgRuntimePort := 0
  97. if *httpEnable {
  98. httpRuntimePort = *httpPort
  99. }
  100. if *pgEnable {
  101. pgRuntimePort = *pgPort
  102. }
  103. if err := pizzaruntime.WritePizzaSQL(os.Getpid(), httpRuntimePort, pgRuntimePort); err != nil {
  104. fmt.Fprintf(os.Stderr, "Failed to write runtime info: %v\n", err)
  105. }
  106. runServers()
  107. return
  108. }
  109. // Check for export command
  110. if *exportFile != "" {
  111. runExport()
  112. return
  113. }
  114. // Check for import command
  115. if *importFile != "" {
  116. runImport()
  117. return
  118. }
  119. // Check for command-line SQL
  120. args := flag.Args()
  121. if len(args) > 0 {
  122. // Execute single SQL statement
  123. sql := strings.Join(args, " ")
  124. executeSingle(sql)
  125. return
  126. }
  127. // Check for piped input
  128. stat, _ := os.Stdin.Stat()
  129. if (stat.Mode() & os.ModeCharDevice) == 0 {
  130. // Input is from pipe
  131. executePipe()
  132. return
  133. }
  134. // Interactive REPL mode
  135. runREPL()
  136. }
  137. func executeSingle(sql string) {
  138. // Try to connect to PizzaKV
  139. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  140. if err != nil {
  141. // Fall back to expression-only mode
  142. executeExpressionOnly(sql)
  143. return
  144. }
  145. defer pool.Close()
  146. schema := storage.NewSchemaManager(pool, *database)
  147. table := storage.NewTableManager(pool, schema, *database)
  148. exec := executor.New(schema, table)
  149. exec.SyncCatalog()
  150. result, err := executeSQL(exec, sql)
  151. if err != nil {
  152. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  153. os.Exit(1)
  154. }
  155. fmt.Print(result.String())
  156. }
  157. func executePipe() {
  158. // Try to connect to PizzaKV
  159. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  160. if err != nil {
  161. // Fall back to expression-only mode
  162. scanner := bufio.NewScanner(os.Stdin)
  163. for scanner.Scan() {
  164. sql := strings.TrimSpace(scanner.Text())
  165. if sql == "" || strings.HasPrefix(sql, "--") {
  166. continue
  167. }
  168. executeExpressionOnly(sql)
  169. }
  170. return
  171. }
  172. defer pool.Close()
  173. schema := storage.NewSchemaManager(pool, *database)
  174. table := storage.NewTableManager(pool, schema, *database)
  175. exec := executor.New(schema, table)
  176. exec.SyncCatalog()
  177. scanner := bufio.NewScanner(os.Stdin)
  178. for scanner.Scan() {
  179. sql := strings.TrimSpace(scanner.Text())
  180. if sql == "" || strings.HasPrefix(sql, "--") {
  181. continue
  182. }
  183. result, err := executeSQL(exec, sql)
  184. if err != nil {
  185. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  186. continue
  187. }
  188. fmt.Print(result.String())
  189. }
  190. }
  191. func runREPL() {
  192. fmt.Println("PizzaSQL - SQL-92 compatible database")
  193. fmt.Printf("Build: %s\n", version.String())
  194. fmt.Println("Type 'help' for usage, 'quit' to exit")
  195. fmt.Println()
  196. // Try to connect to PizzaKV
  197. var pool *storage.KVPool
  198. var schema *storage.SchemaManager
  199. var table *storage.TableManager
  200. var exec *executor.Executor
  201. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  202. if err != nil {
  203. fmt.Printf("Warning: Cannot connect to PizzaKV at %s\n", *kvAddr)
  204. fmt.Println("Running in expression-only mode (no table storage)")
  205. fmt.Println()
  206. } else {
  207. schema = storage.NewSchemaManager(pool, *database)
  208. table = storage.NewTableManager(pool, schema, *database)
  209. exec = executor.New(schema, table)
  210. exec.SyncCatalog()
  211. fmt.Printf("Connected to PizzaKV at %s (database: %s)\n\n", *kvAddr, *database)
  212. }
  213. historyFile := replHistoryFile()
  214. rl, err := readline.NewEx(&readline.Config{
  215. Prompt: "pizzasql> ",
  216. HistoryFile: historyFile,
  217. InterruptPrompt: "^C",
  218. EOFPrompt: "exit",
  219. AutoComplete: newREPLCompleter(schema),
  220. })
  221. if err != nil {
  222. fmt.Fprintf(os.Stderr, "Failed to initialize interactive input: %v\n", err)
  223. return
  224. }
  225. defer rl.Close()
  226. var sqlBuffer strings.Builder
  227. for {
  228. if sqlBuffer.Len() == 0 {
  229. rl.SetPrompt("pizzasql> ")
  230. } else {
  231. rl.SetPrompt(" -> ")
  232. }
  233. line, err := rl.Readline()
  234. if err != nil {
  235. if err == readline.ErrInterrupt {
  236. if sqlBuffer.Len() > 0 {
  237. sqlBuffer.Reset()
  238. fmt.Println("Buffer cleared")
  239. continue
  240. }
  241. fmt.Println("^C")
  242. continue
  243. }
  244. if err == io.EOF {
  245. fmt.Println()
  246. break
  247. }
  248. fmt.Fprintf(os.Stderr, "Input error: %v\n", err)
  249. continue
  250. }
  251. line = strings.TrimSpace(line)
  252. // Handle special commands
  253. switch strings.ToLower(line) {
  254. case "quit", "exit", "\\q":
  255. fmt.Println("Goodbye!")
  256. if pool != nil {
  257. pool.Close()
  258. }
  259. return
  260. case "help", "\\h":
  261. printHelp()
  262. continue
  263. case "tables", "\\dt":
  264. if schema != nil {
  265. listTables(schema)
  266. } else {
  267. fmt.Println("Not connected to database")
  268. }
  269. continue
  270. case "clear", "\\c":
  271. sqlBuffer.Reset()
  272. fmt.Println("Buffer cleared")
  273. continue
  274. case "status", "\\s":
  275. printStatus(exec != nil)
  276. continue
  277. case "functions", "\\df":
  278. printFunctions()
  279. continue
  280. }
  281. // Skip empty lines and comments
  282. if line == "" || strings.HasPrefix(line, "--") {
  283. continue
  284. }
  285. // Accumulate SQL
  286. if sqlBuffer.Len() > 0 {
  287. sqlBuffer.WriteString(" ")
  288. }
  289. sqlBuffer.WriteString(line)
  290. // Check if statement is complete (ends with semicolon)
  291. sql := sqlBuffer.String()
  292. if !strings.HasSuffix(sql, ";") {
  293. continue
  294. }
  295. // Remove semicolon and execute
  296. sql = strings.TrimSuffix(sql, ";")
  297. sqlBuffer.Reset()
  298. if exec != nil {
  299. result, err := executeSQL(exec, sql)
  300. if err != nil {
  301. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  302. continue
  303. }
  304. fmt.Print(result.String())
  305. } else {
  306. executeExpressionOnly(sql)
  307. }
  308. }
  309. }
  310. type replCompleter struct {
  311. getTables func() []string
  312. }
  313. func newREPLCompleter(schema *storage.SchemaManager) readline.AutoCompleter {
  314. return &replCompleter{
  315. getTables: func() []string {
  316. if schema == nil {
  317. return nil
  318. }
  319. tables, err := schema.ListTables()
  320. if err != nil {
  321. return nil
  322. }
  323. return tables
  324. },
  325. }
  326. }
  327. func (c *replCompleter) Do(line []rune, pos int) ([][]rune, int) {
  328. if pos > len(line) {
  329. pos = len(line)
  330. }
  331. fragment := string(line[:pos])
  332. start := pos
  333. for start > 0 {
  334. r := line[start-1]
  335. if !(r == '_' || r == '\\' || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9')) {
  336. break
  337. }
  338. start--
  339. }
  340. prefix := fragment[start:pos]
  341. prefixUpper := strings.ToUpper(prefix)
  342. candidates := append(replCommands(), sqlKeywords()...)
  343. candidates = append(candidates, c.getTables()...)
  344. seen := make(map[string]struct{}, len(candidates))
  345. var out [][]rune
  346. for _, cand := range candidates {
  347. cand = strings.TrimSpace(cand)
  348. if cand == "" {
  349. continue
  350. }
  351. upper := strings.ToUpper(cand)
  352. if _, ok := seen[upper]; ok {
  353. continue
  354. }
  355. seen[upper] = struct{}{}
  356. if prefixUpper == "" || strings.HasPrefix(upper, prefixUpper) {
  357. suffix := cand
  358. if len(prefix) > 0 && len(cand) >= len(prefix) && strings.EqualFold(cand[:len(prefix)], prefix) {
  359. suffix = cand[len(prefix):]
  360. }
  361. suffix = matchSuffixCase(prefix, suffix)
  362. out = append(out, []rune(suffix))
  363. }
  364. }
  365. return out, len(prefix)
  366. }
  367. func replHistoryFile() string {
  368. home, err := os.UserHomeDir()
  369. if err != nil || home == "" {
  370. return ".pizzasql_history"
  371. }
  372. return filepath.Join(home, ".pizzasql_history")
  373. }
  374. func replCommands() []string {
  375. return []string{"help", "quit", "exit", "tables", "clear", "status", "functions", "\\h", "\\q", "\\dt", "\\c", "\\s", "\\df"}
  376. }
  377. func sqlKeywords() []string {
  378. return []string{
  379. "SELECT", "FROM", "WHERE", "INSERT", "INTO", "VALUES", "UPDATE", "SET", "DELETE",
  380. "CREATE", "TABLE", "DROP", "ALTER", "INDEX", "VIEW", "JOIN", "LEFT", "RIGHT", "INNER",
  381. "ON", "GROUP", "BY", "ORDER", "LIMIT", "OFFSET", "HAVING", "DISTINCT", "AS", "AND", "OR",
  382. "NOT", "NULL", "TRUE", "FALSE", "PRAGMA", "BEGIN", "COMMIT", "ROLLBACK", "PIZZASQL_VERSION",
  383. }
  384. }
  385. func matchSuffixCase(prefix, suffix string) string {
  386. if prefix == "" || suffix == "" {
  387. return suffix
  388. }
  389. hasLetter := false
  390. allUpper := true
  391. allLower := true
  392. for _, r := range prefix {
  393. if r >= 'A' && r <= 'Z' {
  394. hasLetter = true
  395. allLower = false
  396. continue
  397. }
  398. if r >= 'a' && r <= 'z' {
  399. hasLetter = true
  400. allUpper = false
  401. continue
  402. }
  403. }
  404. if !hasLetter {
  405. return suffix
  406. }
  407. if allUpper {
  408. return strings.ToUpper(suffix)
  409. }
  410. if allLower {
  411. return strings.ToLower(suffix)
  412. }
  413. return suffix
  414. }
  415. func executeSQL(exec *executor.Executor, sql string) (*executor.Result, error) {
  416. l := lexer.New(sql)
  417. p := parser.New(l)
  418. stmt, err := p.Parse()
  419. if err != nil {
  420. return nil, fmt.Errorf("parse error: %w", err)
  421. }
  422. return exec.Execute(stmt)
  423. }
  424. func executeExpressionOnly(sql string) {
  425. l := lexer.New(sql)
  426. p := parser.New(l)
  427. stmt, err := p.Parse()
  428. if err != nil {
  429. fmt.Fprintf(os.Stderr, "Parse error: %v\n", err)
  430. return
  431. }
  432. // For SELECT statements without FROM, we can evaluate expressions
  433. if sel, ok := stmt.(*parser.SelectStmt); ok && len(sel.From) == 0 {
  434. exec := &executor.Executor{}
  435. result, err := executeSelectExpr(exec, sel)
  436. if err != nil {
  437. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  438. return
  439. }
  440. fmt.Print(result.String())
  441. return
  442. }
  443. // For other statements, just print what was parsed
  444. switch s := stmt.(type) {
  445. case *parser.SelectStmt:
  446. fmt.Printf("SELECT statement with %d columns\n", len(s.Columns))
  447. if len(s.From) > 0 {
  448. fmt.Printf(" FROM: %s\n", s.From[0].Name)
  449. }
  450. if s.Where != nil {
  451. fmt.Println(" WHERE: <condition>")
  452. }
  453. fmt.Println("(Not connected to database - cannot execute)")
  454. case *parser.InsertStmt:
  455. fmt.Printf("INSERT into %s (%d rows)\n", s.Table.Name, len(s.Values))
  456. fmt.Println("(Not connected to database - cannot execute)")
  457. case *parser.UpdateStmt:
  458. fmt.Printf("UPDATE %s (%d assignments)\n", s.Table.Name, len(s.Set))
  459. fmt.Println("(Not connected to database - cannot execute)")
  460. case *parser.DeleteStmt:
  461. fmt.Printf("DELETE from %s\n", s.Table.Name)
  462. fmt.Println("(Not connected to database - cannot execute)")
  463. case *parser.CreateTableStmt:
  464. fmt.Printf("CREATE TABLE %s (%d columns)\n", s.Table.Name, len(s.Columns))
  465. fmt.Println("(Not connected to database - cannot execute)")
  466. case *parser.DropTableStmt:
  467. fmt.Printf("DROP TABLE %s\n", s.Tables[0].Name)
  468. fmt.Println("(Not connected to database - cannot execute)")
  469. default:
  470. fmt.Printf("Parsed: %T\n", stmt)
  471. }
  472. }
  473. // executeSelectExpr handles SELECT without FROM (expression evaluation)
  474. func executeSelectExpr(exec *executor.Executor, stmt *parser.SelectStmt) (*executor.Result, error) {
  475. result := executor.NewResult("SELECT")
  476. // Determine columns
  477. for i, col := range stmt.Columns {
  478. if col.Alias != "" {
  479. result.AddColumn(col.Alias)
  480. } else {
  481. result.AddColumn(fmt.Sprintf("column%d", i+1))
  482. }
  483. }
  484. // Evaluate expressions using reflection to access private method
  485. // For simplicity, we'll use a minimal evaluator here
  486. values := make([]interface{}, len(stmt.Columns))
  487. for i, col := range stmt.Columns {
  488. val, err := evalExprSimple(col.Expr)
  489. if err != nil {
  490. return nil, err
  491. }
  492. values[i] = val
  493. }
  494. result.AddRow(values...)
  495. return result, nil
  496. }
  497. // evalExprSimple is a simplified expression evaluator for standalone expressions
  498. func evalExprSimple(expr parser.Expr) (interface{}, error) {
  499. switch e := expr.(type) {
  500. case *parser.LiteralExpr:
  501. switch e.Type {
  502. case lexer.TokenNumber:
  503. if strings.Contains(e.Value, ".") {
  504. var f float64
  505. fmt.Sscanf(e.Value, "%f", &f)
  506. return f, nil
  507. }
  508. var i int64
  509. fmt.Sscanf(e.Value, "%d", &i)
  510. return i, nil
  511. case lexer.TokenString:
  512. return e.Value, nil
  513. case lexer.TokenNULL:
  514. return nil, nil
  515. case lexer.TokenTRUE:
  516. return true, nil
  517. case lexer.TokenFALSE:
  518. return false, nil
  519. }
  520. case *parser.BinaryExpr:
  521. left, err := evalExprSimple(e.Left)
  522. if err != nil {
  523. return nil, err
  524. }
  525. right, err := evalExprSimple(e.Right)
  526. if err != nil {
  527. return nil, err
  528. }
  529. return evalBinarySimple(e.Op, left, right)
  530. case *parser.UnaryExpr:
  531. val, err := evalExprSimple(e.Operand)
  532. if err != nil {
  533. return nil, err
  534. }
  535. switch e.Op {
  536. case lexer.TokenMinus:
  537. return -toFloatSimple(val), nil
  538. case lexer.TokenNOT:
  539. return !toBoolSimple(val), nil
  540. }
  541. return val, nil
  542. case *parser.ParenExpr:
  543. return evalExprSimple(e.Expr)
  544. case *parser.FunctionCall:
  545. switch strings.ToUpper(e.Name) {
  546. case "PIZZASQL_VERSION", "SQLITE_VERSION":
  547. return version.String(), nil
  548. default:
  549. return nil, fmt.Errorf("unsupported function in expression mode: %s", e.Name)
  550. }
  551. }
  552. return nil, fmt.Errorf("unsupported expression type: %T", expr)
  553. }
  554. func evalBinarySimple(op lexer.TokenType, left, right interface{}) (interface{}, error) {
  555. switch op {
  556. case lexer.TokenPlus:
  557. return toFloatSimple(left) + toFloatSimple(right), nil
  558. case lexer.TokenMinus:
  559. return toFloatSimple(left) - toFloatSimple(right), nil
  560. case lexer.TokenStar:
  561. return toFloatSimple(left) * toFloatSimple(right), nil
  562. case lexer.TokenSlash:
  563. r := toFloatSimple(right)
  564. if r == 0 {
  565. return nil, nil
  566. }
  567. return toFloatSimple(left) / r, nil
  568. case lexer.TokenEq:
  569. return compareSimple(left, right) == 0, nil
  570. case lexer.TokenNeq:
  571. return compareSimple(left, right) != 0, nil
  572. case lexer.TokenLt:
  573. return compareSimple(left, right) < 0, nil
  574. case lexer.TokenGt:
  575. return compareSimple(left, right) > 0, nil
  576. case lexer.TokenLte:
  577. return compareSimple(left, right) <= 0, nil
  578. case lexer.TokenGte:
  579. return compareSimple(left, right) >= 0, nil
  580. case lexer.TokenAND:
  581. return toBoolSimple(left) && toBoolSimple(right), nil
  582. case lexer.TokenOR:
  583. return toBoolSimple(left) || toBoolSimple(right), nil
  584. }
  585. return nil, fmt.Errorf("unsupported operator: %v", op)
  586. }
  587. func toFloatSimple(v interface{}) float64 {
  588. switch val := v.(type) {
  589. case int64:
  590. return float64(val)
  591. case float64:
  592. return val
  593. case bool:
  594. if val {
  595. return 1
  596. }
  597. return 0
  598. }
  599. return 0
  600. }
  601. func toBoolSimple(v interface{}) bool {
  602. switch val := v.(type) {
  603. case bool:
  604. return val
  605. case int64:
  606. return val != 0
  607. case float64:
  608. return val != 0
  609. }
  610. return false
  611. }
  612. func compareSimple(a, b interface{}) int {
  613. fa := toFloatSimple(a)
  614. fb := toFloatSimple(b)
  615. if fa < fb {
  616. return -1
  617. }
  618. if fa > fb {
  619. return 1
  620. }
  621. return 0
  622. }
  623. func printHelp() {
  624. fmt.Println("PizzaSQL Commands:")
  625. fmt.Println(" help, \\h Show this help")
  626. fmt.Println(" quit, \\q Exit the program")
  627. fmt.Println(" tables, \\dt List all tables")
  628. fmt.Println(" clear, \\c Clear the input buffer")
  629. fmt.Println(" status, \\s Show build and connection status")
  630. fmt.Println(" functions, \\df List built-in SQL functions")
  631. fmt.Println()
  632. fmt.Println("SQL Statements (end with semicolon):")
  633. fmt.Println(" SELECT ... FROM ... WHERE ...")
  634. fmt.Println(" INSERT INTO table (cols) VALUES (...)")
  635. fmt.Println(" UPDATE table SET col = val WHERE ...")
  636. fmt.Println(" DELETE FROM table WHERE ...")
  637. fmt.Println(" CREATE TABLE table (col TYPE, ...)")
  638. fmt.Println(" DROP TABLE table")
  639. fmt.Println()
  640. fmt.Println("Expression Mode (SELECT without FROM):")
  641. fmt.Println(" SELECT 1 + 2 * 3;")
  642. fmt.Println(" SELECT UPPER('hello');")
  643. fmt.Println()
  644. fmt.Println("Export/Import:")
  645. fmt.Println(" pizzasql -db mydb -o backup.sql Export database to SQL file")
  646. fmt.Println(" pizzasql -db mydb -table users -o t.sql Export single table")
  647. fmt.Println(" pizzasql -db mydb -o backup.sql -drop Include DROP TABLE statements")
  648. fmt.Println(" pizzasql -db mydb -i backup.sql Import SQL file")
  649. fmt.Println(" pizzasql -db mydb -i source.db Import SQLite .db file (auto-detected)")
  650. fmt.Println(" pizzasql -db mydb -i source.db -ignore-errors Import, skip errors")
  651. fmt.Println()
  652. fmt.Println("CSV Format:")
  653. fmt.Println(" pizzasql -db mydb -table users -o users.csv Export table to CSV")
  654. fmt.Println(" pizzasql -db mydb -table users -i users.csv Import CSV to table")
  655. fmt.Println(" pizzasql -db mydb -table new -i data.csv -create-table Create table from CSV")
  656. }
  657. func listTables(schema *storage.SchemaManager) {
  658. tables, err := schema.ListTables()
  659. if err != nil {
  660. fmt.Fprintf(os.Stderr, "Error: %v\n", err)
  661. return
  662. }
  663. if len(tables) == 0 {
  664. fmt.Println("No tables found")
  665. return
  666. }
  667. fmt.Println("Tables:")
  668. for _, t := range tables {
  669. fmt.Printf(" %s\n", t)
  670. }
  671. }
  672. func printStatus(connected bool) {
  673. fmt.Printf("version: %s\n", version.String())
  674. if connected {
  675. fmt.Println("storage: connected")
  676. return
  677. }
  678. fmt.Println("storage: expression-only mode")
  679. }
  680. func printFunctions() {
  681. fns := analyzer.BuiltinFunctions()
  682. fmt.Println("Built-in SQL functions:")
  683. for _, fn := range fns {
  684. kind := "scalar"
  685. if fn.IsAggregate {
  686. kind = "aggregate"
  687. }
  688. if fn.MaxArgs < 0 {
  689. fmt.Printf(" %-18s %s (args: %d+)\n", fn.Name, kind, fn.MinArgs)
  690. continue
  691. }
  692. if fn.MinArgs == fn.MaxArgs {
  693. fmt.Printf(" %-18s %s (args: %d)\n", fn.Name, kind, fn.MinArgs)
  694. continue
  695. }
  696. fmt.Printf(" %-18s %s (args: %d..%d)\n", fn.Name, kind, fn.MinArgs, fn.MaxArgs)
  697. }
  698. }
  699. func runExport() {
  700. // Connect to PizzaKV
  701. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  702. if err != nil {
  703. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  704. os.Exit(1)
  705. }
  706. defer pool.Close()
  707. schema := storage.NewSchemaManager(pool, *database)
  708. table := storage.NewTableManager(pool, schema, *database)
  709. // Determine format from flag or file extension
  710. format := strings.ToLower(*exportFormat)
  711. if format == "" {
  712. format = detectFileFormat(*exportFile)
  713. }
  714. switch format {
  715. case "csv":
  716. // CSV export requires a table name
  717. if *exportTable == "" {
  718. fmt.Fprintf(os.Stderr, "CSV export requires -table flag\n")
  719. os.Exit(1)
  720. }
  721. csvOpts := csvexport.DefaultExportOptions()
  722. csvOpts.Table = *exportTable
  723. data, err := csvexport.ExportTableToBytes(schema, table, csvOpts)
  724. if err != nil {
  725. fmt.Fprintf(os.Stderr, "Export failed: %v\n", err)
  726. os.Exit(1)
  727. }
  728. err = os.WriteFile(*exportFile, data, 0644)
  729. if err != nil {
  730. fmt.Fprintf(os.Stderr, "Failed to write file: %v\n", err)
  731. os.Exit(1)
  732. }
  733. fmt.Printf("Exported table '%s' to %s (CSV)\n", *exportTable, *exportFile)
  734. default: // sql, sqlite
  735. // Configure export options
  736. opts := sqlexport.ExportOptions{
  737. IncludeData: true,
  738. DropTables: *exportDrop,
  739. }
  740. if *exportTable != "" {
  741. opts.Tables = []string{*exportTable}
  742. }
  743. // Export database
  744. sql, err := sqlexport.ExportDatabase(schema, table, opts)
  745. if err != nil {
  746. fmt.Fprintf(os.Stderr, "Export failed: %v\n", err)
  747. os.Exit(1)
  748. }
  749. // Write to file
  750. err = os.WriteFile(*exportFile, []byte(sql), 0644)
  751. if err != nil {
  752. fmt.Fprintf(os.Stderr, "Failed to write file: %v\n", err)
  753. os.Exit(1)
  754. }
  755. fmt.Printf("Exported database '%s' to %s\n", *database, *exportFile)
  756. }
  757. }
  758. func runImport() {
  759. // Connect to PizzaKV
  760. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  761. if err != nil {
  762. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  763. os.Exit(1)
  764. }
  765. defer pool.Close()
  766. schema := storage.NewSchemaManager(pool, *database)
  767. table := storage.NewTableManager(pool, schema, *database)
  768. exec := executor.New(schema, table)
  769. exec.SyncCatalog()
  770. // Read file
  771. data, err := os.ReadFile(*importFile)
  772. if err != nil {
  773. fmt.Fprintf(os.Stderr, "Failed to read file: %v\n", err)
  774. os.Exit(1)
  775. }
  776. // Determine format from flag or file extension
  777. format := strings.ToLower(*exportFormat)
  778. if format == "" {
  779. format = detectFileFormat(*importFile)
  780. }
  781. switch format {
  782. case "csv":
  783. // CSV import requires a table name
  784. if *exportTable == "" {
  785. fmt.Fprintf(os.Stderr, "CSV import requires -table flag\n")
  786. os.Exit(1)
  787. }
  788. csvOpts := csvimport.DefaultImportOptions()
  789. csvOpts.TableName = *exportTable
  790. csvOpts.IgnoreErrors = *ignoreErrors
  791. csvOpts.CreateTable = *createTable
  792. result, err := csvimport.ImportCSV(strings.NewReader(string(data)), schema, table, csvOpts)
  793. if err != nil {
  794. fmt.Fprintf(os.Stderr, "Import failed: %v\n", err)
  795. if len(result.Errors) > 0 {
  796. fmt.Fprintf(os.Stderr, "Errors:\n")
  797. for _, e := range result.Errors {
  798. fmt.Fprintf(os.Stderr, " - %s\n", e)
  799. }
  800. }
  801. os.Exit(1)
  802. }
  803. fmt.Printf("CSV import completed successfully\n")
  804. fmt.Printf(" Rows imported: %d\n", result.RowsImported)
  805. if result.RowsSkipped > 0 {
  806. fmt.Printf(" Rows skipped: %d\n", result.RowsSkipped)
  807. }
  808. if result.TableCreated {
  809. fmt.Printf(" Table created: %s\n", *exportTable)
  810. }
  811. if len(result.Errors) > 0 {
  812. fmt.Printf(" Warnings/Errors: %d\n", len(result.Errors))
  813. for _, e := range result.Errors {
  814. fmt.Printf(" - %s\n", e)
  815. }
  816. }
  817. case "sqlite":
  818. // Binary SQLite .db import
  819. opts := sqliteimport.DefaultImportOptions()
  820. opts.IgnoreErrors = *ignoreErrors
  821. result, err := sqliteimport.ImportSQLiteFile(*importFile, exec, opts)
  822. if err != nil {
  823. fmt.Fprintf(os.Stderr, "Import failed: %v\n", err)
  824. if len(result.Errors) > 0 {
  825. fmt.Fprintf(os.Stderr, "Errors:\n")
  826. for _, e := range result.Errors {
  827. fmt.Fprintf(os.Stderr, " - %s\n", e)
  828. }
  829. }
  830. os.Exit(1)
  831. }
  832. fmt.Printf("SQLite import completed successfully\n")
  833. if len(result.TablesCreated) > 0 {
  834. fmt.Printf(" Tables created: %s\n", strings.Join(result.TablesCreated, ", "))
  835. }
  836. if len(result.TablesImported) > 0 {
  837. fmt.Printf(" Tables imported: %s\n", strings.Join(result.TablesImported, ", "))
  838. }
  839. fmt.Printf(" Rows inserted: %d\n", result.RowsInserted)
  840. if result.IndexesCreated > 0 {
  841. fmt.Printf(" Indexes created: %d\n", result.IndexesCreated)
  842. }
  843. if len(result.Errors) > 0 {
  844. fmt.Printf(" Warnings/Errors: %d\n", len(result.Errors))
  845. for _, e := range result.Errors {
  846. fmt.Printf(" - %s\n", e)
  847. }
  848. }
  849. default: // sql
  850. // Configure import options
  851. opts := sqlimport.ImportOptions{
  852. IgnoreErrors: *ignoreErrors,
  853. }
  854. // Import SQL
  855. result, err := sqlimport.ImportSQL(exec, string(data), opts)
  856. if err != nil {
  857. fmt.Fprintf(os.Stderr, "Import failed: %v\n", err)
  858. if len(result.Errors) > 0 {
  859. fmt.Fprintf(os.Stderr, "Errors:\n")
  860. for _, e := range result.Errors {
  861. fmt.Fprintf(os.Stderr, " - %s\n", e)
  862. }
  863. }
  864. os.Exit(1)
  865. }
  866. fmt.Printf("Import completed successfully\n")
  867. fmt.Printf(" Statements executed: %d\n", result.StatementsExecuted)
  868. if len(result.TablesCreated) > 0 {
  869. fmt.Printf(" Tables created: %s\n", strings.Join(result.TablesCreated, ", "))
  870. }
  871. if len(result.TablesDropped) > 0 {
  872. fmt.Printf(" Tables dropped: %s\n", strings.Join(result.TablesDropped, ", "))
  873. }
  874. fmt.Printf(" Rows inserted: %d\n", result.RowsInserted)
  875. if len(result.Errors) > 0 {
  876. fmt.Printf(" Warnings/Errors: %d\n", len(result.Errors))
  877. for _, e := range result.Errors {
  878. fmt.Printf(" - %s\n", e)
  879. }
  880. }
  881. }
  882. }
  883. func detectFileFormat(filename string) string {
  884. lower := strings.ToLower(filename)
  885. if strings.HasSuffix(lower, ".csv") {
  886. return "csv"
  887. }
  888. if strings.HasSuffix(lower, ".db") || strings.HasSuffix(lower, ".sqlite") || strings.HasSuffix(lower, ".sqlite3") {
  889. return "sqlite"
  890. }
  891. return "sql"
  892. }
  893. func runServers() {
  894. pool, err := storage.NewKVPool(*kvAddr, *poolSize, *timeout)
  895. if err != nil {
  896. fmt.Fprintf(os.Stderr, "Failed to connect to PizzaKV at %s: %v\n", *kvAddr, err)
  897. os.Exit(1)
  898. }
  899. defer pool.Close()
  900. dbManagerConfig := &storage.DatabaseManagerConfig{
  901. DefaultDatabase: *database,
  902. AutoCreate: true,
  903. }
  904. dbManager := storage.NewDatabaseManager(pool, dbManagerConfig)
  905. stop := make(chan os.Signal, 1)
  906. signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
  907. var httpSrv *httpserver.Server
  908. var pprofSrv *http.Server
  909. var pgSrv *pgserver.Server
  910. if *httpEnable {
  911. config := httpserver.DefaultConfig()
  912. config.Host = *httpHost
  913. config.Port = *httpPort
  914. config.EnableCORS = *httpCORS
  915. config.EnableAuth = *httpAuth
  916. config.EnableCompression = *httpCompression
  917. config.EnableLogging = !*quiet
  918. if *apiKeys != "" {
  919. config.APIKeys = strings.Split(*apiKeys, ",")
  920. }
  921. httpSrv = httpserver.NewWithDatabaseManager(config, dbManager)
  922. if startPprofServerHook != nil {
  923. pprofSrv = startPprofServerHook()
  924. }
  925. go func() {
  926. if err := httpSrv.Start(); err != nil && err != http.ErrServerClosed {
  927. fmt.Fprintf(os.Stderr, "HTTP server error: %v\n", err)
  928. os.Exit(1)
  929. }
  930. }()
  931. fmt.Printf("HTTP http://%s:%d\n", *httpHost, *httpPort)
  932. }
  933. if *pgEnable {
  934. config := pgserver.DefaultConfig()
  935. config.Host = *pgHost
  936. config.Port = *pgPort
  937. config.DefaultDatabase = *database
  938. config.Quiet = *quiet
  939. pgSrv = pgserver.New(config, dbManager)
  940. go func() {
  941. if err := pgSrv.Start(); err != nil {
  942. fmt.Fprintf(os.Stderr, "PostgreSQL server error: %v\n", err)
  943. os.Exit(1)
  944. }
  945. }()
  946. fmt.Printf("PG postgresql://%s:%d/%s\n", *pgHost, *pgPort, *database)
  947. }
  948. fmt.Printf("KV %s\n", *kvAddr)
  949. fmt.Printf("DB %s\n", *database)
  950. fmt.Println("Press Ctrl+C to stop")
  951. <-stop
  952. fmt.Println("\nShutting down...")
  953. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  954. defer cancel()
  955. if httpSrv != nil {
  956. if err := httpSrv.Shutdown(ctx); err != nil {
  957. fmt.Fprintf(os.Stderr, "HTTP shutdown error: %v\n", err)
  958. }
  959. }
  960. if pprofSrv != nil {
  961. pprofSrv.Shutdown(ctx)
  962. }
  963. if pgSrv != nil {
  964. if err := pgSrv.Shutdown(ctx); err != nil {
  965. fmt.Fprintf(os.Stderr, "PG shutdown error: %v\n", err)
  966. }
  967. }
  968. }
  969. // launchPizzaKV starts a dedicated PizzaKV instance for this pizzasql process.
  970. func launchPizzaKV() error {
  971. if _, err := os.Stat(".db"); err == nil {
  972. if !*forceYes {
  973. if live := pizzaruntime.LiveInstances(); len(live) > 0 {
  974. inst := live[0]
  975. kvAddr := "<addr>"
  976. if inst.PizzaKV != nil {
  977. kvAddr = inst.PizzaKV.Addr
  978. }
  979. return fmt.Errorf(".db file already exists and another pizzasql instance is running (PID %d)\n"+
  980. " To connect to its pizzakv: pizzasql -kvaddr=%s\n"+
  981. " To start fresh (removes data): rm .db && pizzasql -kv\n"+
  982. " To run a separate instance: cd /other/dir && pizzasql -kv",
  983. inst.PizzaSQL.PID, kvAddr)
  984. }
  985. }
  986. }
  987. kvManager = kvmanager.NewManager()
  988. fmt.Println("Starting PizzaKV...")
  989. info, err := kvManager.Start(*kvFlags)
  990. if err != nil {
  991. return err
  992. }
  993. fmt.Printf("PizzaKV started on %s (PID: %d)\n", info.Addr, info.PID)
  994. fmt.Printf("Runtime: %s\n", pizzaruntime.File)
  995. fmt.Println("PizzaKV is ready!")
  996. *kvAddr = info.Addr
  997. return nil
  998. }
  999. // stopPizzaKV stops the managed PizzaKV instance
  1000. func stopPizzaKV() {
  1001. if kvManager != nil {
  1002. fmt.Println("Stopping PizzaKV...")
  1003. if err := kvManager.Stop(); err != nil {
  1004. fmt.Fprintf(os.Stderr, "Error stopping PizzaKV: %v\n", err)
  1005. } else {
  1006. fmt.Println("PizzaKV stopped")
  1007. }
  1008. }
  1009. }