2
0

main.go 30 KB

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