Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From fa492ff57ca9d89a To d2446e50805cc3a4
2022-04-04
| ||
17:27 | For shell extension writers, reduce boilerplate (mimicing SQLITE_EXTENSION_INIT# macros) (check-in: 761208132d user: larrybr tags: cli_extension) | |
06:33 | For TCL extension: Adjust provided "gui" command, and document it. For shell: Separate shell variables from binding parameters, mainly so they live longer, in the shell DB. Add .vars dot command to reflect this separation, and specialized for shell variables. Much code shuffling to share code between .parameters and .vars commands. (check-in: fa492ff57c user: larrybr tags: cli_extension) | |
2022-03-31
| ||
04:05 | Make CLI build after an incomplete rename completed. (check-in: 4f11e639f0 user: larrybr tags: cli_extension) | |
03:45 | For TCL extension: Add Tk and means to run it, optionally. Implement ScriptSupport interface. For shell: Get scripting support thru an object interface like the others. Rename OutMode* to Export*. For both: Provide a way to pass arguments to an extension upon load and get them into TCL's argv. (check-in: d2446e5080 user: larrybr tags: cli_extension) | |
2022-03-29
| ||
21:11 | For TCL extension: Cleanup TCL library interaction and interpreter management. Group code into more purpose oriented functions. Add .unknown dot command, delegating to TCL commands with "dot" names. Drop planned .eval new dot command. For shell: Implement undocumented .unknown dot command, doing little. Make dispatcher call .unknown's implementation for unknown dot command. Fix a command lookup bug exposed by above change to unknown handling. Add .eval dot command. Add options to .x command aimed at control of errors in command sequences. (a WIP) Make dispatcher report ambiguous dot command lookups, even with no extension. (check-in: 7616a6f4ab user: larrybr tags: cli_extension) | |
Changes to ext/misc/tclshext.c.in.
︙ | ︙ | |||
12 13 14 15 16 17 18 | ** This file contains code to implement the "tclshext" shell extension ** for use with the extensible "sqlite3" CLI shell. On *Nix, build thusly: tool/mkshellc.tcl ext/misc/tclshext.c.in > tclshext.c gcc -shared -fPIC -O2 -I. -Isrc -I/usr/include/tcl8.6 tclshext.c \ -o tclshext.so -ltcl8.6 ** Later TCL versions can be used if desired. "TCL scripting support is added with a registerScripting() call in the\n" | | | | | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ** This file contains code to implement the "tclshext" shell extension ** for use with the extensible "sqlite3" CLI shell. On *Nix, build thusly: tool/mkshellc.tcl ext/misc/tclshext.c.in > tclshext.c gcc -shared -fPIC -O2 -I. -Isrc -I/usr/include/tcl8.6 tclshext.c \ -o tclshext.so -ltcl8.6 ** Later TCL versions can be used if desired. "TCL scripting support is added with a registerScripting() call in the\n" "ShellExtensionAPI, as documented for ScriptingSupport interface. This\n" "support lasts until the scripting object destructor is called. Until\n" */ static const char * const zTclHelp = "This extension adds these features to the host shell:\n" " 1. TCL scripting support is added.\n" " 2. TCL commands are added: udb shdb now_interactive get_tcl_group ..\n" " 3. The .tcl and .unknown dot commands are added.\n" " 4. If built with Tk capability, a run_gui TCL command may be added if\n" " the extension is loaded by the shell via .load ... -shext -tk . Any\n" " other arguments beyond -shext are in TCL's argv variable.\n" "Operation:\n" " Shell input groups beginning with \"..\" are treated as TCL input, in\n" " these ways: (1) When a bare \"..\" is entered, a TCL REPL loop is run\n" " until the end of input is seen; (2) When \"..D ...\" is entered, (where\n" " \"D\" is a dot-command name), the D dot command will be run in its normal\n" " fashion, but its arguments will be collected according to TCL parsing\n" " rules then expanded as usual for TCL commands; and (3) when \".. T ...\"\n" |
︙ | ︙ | |||
204 205 206 207 208 209 210 | /* Provide help for users of this scripting implementation. */ DERIVED_METHOD(const char *, help, ScriptSupport,TclSS, 1,( int more )){ (void)(pThis); switch( more ){ case 0: return "Provides TCL scripting support for SQLite extensible shell.\n"; | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | /* Provide help for users of this scripting implementation. */ DERIVED_METHOD(const char *, help, ScriptSupport,TclSS, 1,( int more )){ (void)(pThis); switch( more ){ case 0: return "Provides TCL scripting support for SQLite extensible shell.\n"; case 1: return zTclHelp; /* ToDo: Rewrite this help. */ } return 0; } /* Not doing this yet. */ DERIVED_METHOD(int, configure, ScriptSupport,TclSS, 4,( ShellExState *pSES, char **pzErr, |
︙ | ︙ | |||
683 684 685 686 687 688 689 | }else{ Tcl_SetResult(interp, "too many arguments", TCL_STATIC); return TCL_ERROR; } } #ifndef SHELL_OMIT_TK | < < < | | < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | }else{ Tcl_SetResult(interp, "too many arguments", TCL_STATIC); return TCL_ERROR; } } #ifndef SHELL_OMIT_TK static int runTkGUI(void *pvSS, Tcl_Interp *interp, int nArgs, const char *azArgs[]){ ShellExState *psx = (ShellExState *)pvSS; /* This runs without looking at stdin. So it cannot be a REPL, yet. * Unless user has created something for it to do, it does nothing. */ Tk_MainLoop(); } #endif #define UNKNOWN_RENAME "::_original_unknown" /* C implementation of TCL ::unknown to (maybe) delegate to dot commands */ static int unknownDotDelegate(void *pvSS, Tcl_Interp *interp, |
︙ | ︙ | |||
1108 1109 1110 1111 1112 1113 1114 | Tcl_Eval(interp, "rename unknown "UNKNOWN_RENAME); Tcl_CreateCommand(interp, "unknown", unknownDotDelegate, psx, 0); /* Define this proc so that ".." either gets to the TCL REPL loop * or does nothing (if already in it), as a user convenience. */ Tcl_Eval(interp, "proc .. {} {}"); #ifndef SHELL_OMIT_TK if( ldTk ){ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < | < < < < | | < < < < | < | 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 | Tcl_Eval(interp, "rename unknown "UNKNOWN_RENAME); Tcl_CreateCommand(interp, "unknown", unknownDotDelegate, psx, 0); /* Define this proc so that ".." either gets to the TCL REPL loop * or does nothing (if already in it), as a user convenience. */ Tcl_Eval(interp, "proc .. {} {}"); #ifndef SHELL_OMIT_TK if( ldTk ){ /* Create a command which wraps Tk_MainLoop(). It runs a GUI event * loop, so does not return until all of its Tk windows are closed. */ Tcl_CreateCommand(interp, "run_gui", runTkGUI, psx, 0); zAppName = "tclshext_tk"; } #endif Tcl_SetVar2Ex(interp, "argv0", NULL, Tcl_NewStringObj(zAppName,-1), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(tnarg), TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "argv", NULL, targv, TCL_GLOBAL_ONLY); pShExtLink->eid = sqlite3_tclshext_init; } if( rc==SQLITE_OK ){ pShExtLink->extensionDestruct = Tcl_TakeDown; pShExtLink->pvExtensionObject = &interpKeep; }else{ Tcl_TakeDown(&interpKeep); |
︙ | ︙ |
Changes to src/shell.c.in.
︙ | ︙ | |||
1396 1397 1398 1399 1400 1401 1402 | /* shell event subscription list */ int numSubscriptions; /* Number of active entries in below list */ struct EventSubscription { ExtensionId eid; void *pvUserData; ShellEventNotify eventHandler; } *pSubscriptions; /* The current shell event subscriptions */ | < | | | 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 | /* shell event subscription list */ int numSubscriptions; /* Number of active entries in below list */ struct EventSubscription { ExtensionId eid; void *pvUserData; ShellEventNotify eventHandler; } *pSubscriptions; /* The current shell event subscriptions */ #endif ShellExState *pSXS; /* Pointer to companion, exposed shell state */ } ShellInState; /* ** Limit input nesting via .read or any other input redirect. ** It's not too expensive, so a generous allowance can be made. */ #define MAX_INPUT_NESTING 25 /* ** This procedure updates the bSafeMode flag after completion of any ** operation (meta-command or SQL submission) that counts as one for ** which safe mode might be suspended. ** bSafeModeFuture has 3 states salient here: ** equal 0 => Safe mode is and will remain inactive. ** equal 1 => Safe mode is and will remain active. ** N > 1 => Safe mode is suspended for N-1 operations. */ static void updateSafeMode(ShellInState *psi){ switch( psi->bSafeModeFuture ){ |
︙ | ︙ | |||
2410 2411 2412 2413 2414 2415 2416 | if( (psi->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ raw_printf(psi->out, "Progress %u\n", psi->nProgress); } return 0; } #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 | if( (psi->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ raw_printf(psi->out, "Progress %u\n", psi->nProgress); } return 0; } #endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ /* ** Skip over whitespace, returning remainder. */ static const char *skipWhite( const char *z ){ while( IsSpace(*z) ) ++z; return z; } |
︙ | ︙ | |||
3558 3559 3560 3561 3562 3563 3564 | char c = *zName; switch( c ){ case '$': case ':': case '@': case '?': return PTU_Binding; default: return isalpha(c)? PTU_Script : PTU_Nil; } } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < | 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 | char c = *zName; switch( c ){ case '$': case ':': case '@': case '?': return PTU_Binding; default: return isalpha(c)? PTU_Script : PTU_Nil; } } #define PARAM_TABLE_NAME "sqlite_parameters" #define PARAM_TABLE_SCHEMA "temp" #define PARAM_TABLE_SNAME PARAM_TABLE_SCHEMA"."PARAM_TABLE_NAME #ifndef PARAM_STORE_NAME /* Name for table keeping user's saved parameters */ # define PARAM_STORE_NAME "SQLiteShellParameters" #endif #ifndef PARAM_STORE_SCHEMA /* Schema name used to attach saved parameters DB during load/save */ # define PARAM_STORE_SCHEMA "SQLiteShell" #endif #define PARAM_STORE_SNAME PARAM_STORE_SCHEMA"."PARAM_STORE_NAME /* Create the TEMP table used to store parameter bindings and SQL statements */ static void param_table_init(sqlite3 *db){ DbProtectState dps = allow_sys_schema_change(db); sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS "PARAM_TABLE_SNAME"(\n" " key TEXT PRIMARY KEY,\n" " value,\n" " uses INT DEFAULT (0)" /* aka PTU_Binding */ ") WITHOUT ROWID;", 0, 0, 0); restore_sys_schema_protection( db, &dps ); } /* Tell whether the above-created table exists, return true iff exists. */ static int param_table_exists( sqlite3 *db ){ return sqlite3_table_column_metadata (db, PARAM_TABLE_SCHEMA, PARAM_TABLE_NAME, 0, 0, 0, 0, 0, 0)==SQLITE_OK; } /* ** Bind parameters on a prepared statement. ** ** Parameter bindings are taken from a TEMP table of the form: ** ** CREATE TEMP TABLE ** sqlite_parameters(key TEXT PRIMARY KEY, value, uses INT) |
︙ | ︙ | |||
7897 7898 7899 7900 7901 7902 7903 | /* Register a meta-command, to be called during extension load/init. */ static int register_meta_command(ShellExState *p, ExtensionId eid, MetaCommand *pMC){ ShellInState *psi = ISS(p); ShExtInfo *psei = pending_ext_info(psi); const char *zSql | | | 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 | /* Register a meta-command, to be called during extension load/init. */ static int register_meta_command(ShellExState *p, ExtensionId eid, MetaCommand *pMC){ ShellInState *psi = ISS(p); ShExtInfo *psei = pending_ext_info(psi); const char *zSql = "INSERT INTO ShellCommands (name, extIx, cmdIx) VALUES(?, ?, ?)"; int ie = psi->ixExtPending; assert(psi->pShxLoaded!=0 && p->dbShell!=0); if( pMC==0 ) return SQLITE_ERROR; else{ const char *zName = pMC->pMethods->name(pMC); sqlite3_stmt *pStmt; int nc = psei->numMetaCommands; |
︙ | ︙ | |||
7945 7946 7947 7948 7949 7950 7951 | ExtensionId eid, ImportHandler *pIH){ return SQLITE_ERROR; } static int register_scripting(ShellExState *p, ExtensionId eid, ScriptSupport *pSS){ ShellInState *psi = ISS(p); | | < | 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 | ExtensionId eid, ImportHandler *pIH){ return SQLITE_ERROR; } static int register_scripting(ShellExState *p, ExtensionId eid, ScriptSupport *pSS){ ShellInState *psi = ISS(p); if( psi->scriptXid!=0 ){ /* Scripting support already provided. Only one provider is allowed. */ return SQLITE_BUSY; } if( eid==0 || pSS==0 || psi->ixExtPending==0 ){ return SQLITE_MISUSE; } psi->script = pSS; psi->scriptXid = eid; return SQLITE_OK; } |
︙ | ︙ | |||
8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 | break; default: pv = 0; } if( pv==0 ) sqlite3_result_null(context); else sqlite3_result_pointer(context, pv, SHELLEXT_API_POINTERS, 0); } /* Do the initialization needed for use of dbShell for command lookup * and dispatch and for I/O handler lookup and dispatch. */ static int begin_db_dispatch(ShellExState *psx){ ShellInState *psi = ISS(psx); sqlite3_stmt *pStmt = 0; | > > > > > > | < | < < < < < < < > > > > > > > > > > > > > > > > > > | < < < | 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 | break; default: pv = 0; } if( pv==0 ) sqlite3_result_null(context); else sqlite3_result_pointer(context, pv, SHELLEXT_API_POINTERS, 0); } #ifndef SHELL_DB_FILE # define SHELL_DB_STORE ":memory:" #else # define SHELL_DB_STORE SHELL_STRINGIFY(SHELL_DB_FILE) #endif /* Do the initialization needed for use of dbShell for command lookup * and dispatch and for I/O handler lookup and dispatch. */ static int begin_db_dispatch(ShellExState *psx){ ShellInState *psi = ISS(psx); sqlite3_stmt *pStmt = 0; int ic, rc, rc1, rc2; char *zErr = 0; const char *zSql; ShExtInfo sei = {0}; /* Consider: Store these dynamic arrays in the DB as indexed-into blobs. */ assert(psx->dbShell==0 && psi->numExtLoaded==0 && psi->pShxLoaded==0); psi->pShxLoaded = (ShExtInfo *)sqlite3_malloc(2*sizeof(ShExtInfo)); sei.ppMetaCommands = (MetaCommand **)sqlite3_malloc((numCommands+2)*sizeof(MetaCommand *)); sei.ppExportHandlers = (ExportHandler **)sqlite3_malloc(2*sizeof(ExportHandler *)); sei.ppImportHandlers = (ImportHandler **)sqlite3_malloc(2*sizeof(ImportHandler *)); if( sei.ppMetaCommands==0||sei.ppExportHandlers==0||sei.ppImportHandlers==0 || psi->pShxLoaded==0 ){ shell_out_of_memory(); } sei.numExportHandlers = 0; sei.numImportHandlers = 0; for( ic=0; ic<numCommands; ++ic ){ sei.ppMetaCommands[ic] = builtInCommand(ic); } sei.numMetaCommands = ic; psi->pShxLoaded[psi->numExtLoaded++] = sei; rc = sqlite3_open(SHELL_DB_STORE, &psx->dbShell); if( rc!=SQLITE_OK ) return 1; #ifdef SHELL_DB_FILE sqlite3_exec(psx->dbShell, "DROP TABLE IF EXISTS ShellCommands", 0,0,0); sqlite3_exec(psx->dbShell, "DROP VIEW IF EXISTS ActiveCommands", 0,0,0); #endif rc1 = sqlite3_exec(psx->dbShell, "CREATE TABLE ShellCommands(" "name TEXT, extIx INT, cmdIx INT," "PRIMARY KEY(extIx,cmdIx)) WITHOUT ROWID", 0, 0, &zErr); rc2 = sqlite3_exec(psx->dbShell, "CREATE VIEW ActiveCommands AS SELECT " "s.name AS name, max(s.extIx) AS extIx, s.cmdIx AS cmdIx " "FROM ShellCommands s GROUP BY name", 0, 0, &zErr); if( rc1!=SQLITE_OK || rc2!=SQLITE_OK || zErr!=0 ){ utf8_printf(STD_ERR, "Shell DB init failure, %s\n", zErr? zErr : ""); return 1; } zSql = "INSERT INTO ShellCommands (name, extIx, cmdIx) VALUES(?, 0, ?)"; rc1 = sqlite3_prepare_v2(psx->dbShell, zSql, -1, &pStmt, 0); rc2 = sqlite3_exec(psx->dbShell, "BEGIN TRANSACTION", 0, 0, &zErr); if( rc1!=SQLITE_OK || rc2!=SQLITE_OK ) return 1; assert(sei.numMetaCommands>0); for( ic=0; ic<sei.numMetaCommands; ++ic ){ MetaCommand *pmc = sei.ppMetaCommands[ic]; const char *zName = pmc->pMethods->name(pmc); sqlite3_reset(pStmt); sqlite3_bind_text(pStmt, 1, zName, -1, 0); sqlite3_bind_int(pStmt, 2, ic); rc = sqlite3_step(pStmt); if( rc!=SQLITE_DONE ){ sqlite3_exec(psx->dbShell, "ABORT", 0, 0, 0); break; } } sqlite3_finalize(pStmt); if( rc!=SQLITE_DONE ) return 1; rc = sqlite3_exec(psx->dbShell, "COMMIT", 0, 0, &zErr); sqlite3_enable_load_extension(psx->dbShell, 1); return SQLITE_OK; } /* Call one loaded extension's destructors, in reverse order * of their objects' creation, then free the tracking dyna-arrays. */ static void free_one_shext_tracking(ShExtInfo *psei){ |
︙ | ︙ | |||
8277 8278 8279 8280 8281 8282 8283 | ShellInState *psi = ISS(psx); /* save script support state for possible fallback if load fails */ ScriptSupport *pssSave = psi->script; ExtensionId ssiSave = psi->scriptXid; int rc; if( pzErr ) *pzErr = 0; | | | 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 | ShellInState *psi = ISS(psx); /* save script support state for possible fallback if load fails */ ScriptSupport *pssSave = psi->script; ExtensionId ssiSave = psi->scriptXid; int rc; if( pzErr ) *pzErr = 0; if( psx->dbShell==0 ){ rc = begin_db_dispatch(psx); if( rc!=SQLITE_OK ) return rc; assert(ISS(psx)->numExtLoaded==1 && psx->dbShell!=0); } psi->ixExtPending = psi->numExtLoaded; sqlite3_create_function(psx->dbShell, "shext_pointer", 1, SQLITE_DIRECTONLY|SQLITE_UTF8, |
︙ | ︙ | |||
10460 10461 10462 10463 10464 10465 10466 | DISPATCHABLE_COMMAND( nullvalue ? 2 2 ){ sqlite3_snprintf(sizeof(ISS(p)->nullValue), ISS(p)->nullValue, "%.*s", (int)ArraySize(ISS(p)->nullValue)-1, azArg[1]); return DCR_Ok; } | | | | | | 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 | DISPATCHABLE_COMMAND( nullvalue ? 2 2 ){ sqlite3_snprintf(sizeof(ISS(p)->nullValue), ISS(p)->nullValue, "%.*s", (int)ArraySize(ISS(p)->nullValue)-1, azArg[1]); return DCR_Ok; } /* Helper functions for .parameter command */ struct param_row { char * value; int uses; int hits; }; static int param_find_callback(void *pData, int nc, char **pV, char **pC){ assert(nc>=1); assert(strcmp(pC[0],"value")==0); struct param_row *pParam = (struct param_row *)pData; assert(pParam->value==0); /* key values are supposedly unique. */ if( pParam->value!=0 ) sqlite3_free( pParam->value ); pParam->value = smprintf("%s", pV[0]); /* source owned by statement */ if( nc>1 ) pParam->uses = (int)integerValue(pV[1]); ++pParam->hits; return 0; } |
︙ | ︙ | |||
10521 10522 10523 10524 10525 10526 10527 | * If it is an empty list, all parameters will be saved or loaded. * Otherwise, only the named parameters are transferred, if they exist. * It is not an error to specify a name that cannot be transferred * because it does not exist in the source table. * * Returns are SQLITE_OK for success, or other codes for failure. */ | | < | | | | | < < < < < < < | | | | | | | | < | < < < < < < | | < | | | < | | | | | | | | | | | < | < < < < < < | < | | | | | > | < | > | | > | | > | | > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > | 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431 10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463 10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 10480 10481 10482 10483 10484 10485 10486 10487 10488 10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 10499 10500 10501 10502 10503 10504 10505 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 10593 10594 10595 10596 10597 10598 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 | * If it is an empty list, all parameters will be saved or loaded. * Otherwise, only the named parameters are transferred, if they exist. * It is not an error to specify a name that cannot be transferred * because it does not exist in the source table. * * Returns are SQLITE_OK for success, or other codes for failure. */ static int param_xfr_table(sqlite3 *db, const char *zStoreDbName, int bSaveNotLoad, const char *azNames[], int nNames){ int rc = 0; char *zSql = 0; /* to be sqlite3_free()'ed */ sqlite3_str *sbCopy = 0; const char *zHere = PARAM_TABLE_SNAME; const char *zThere = PARAM_STORE_SNAME; const char *zTo = (bSaveNotLoad)? zThere : zHere; const char *zFrom = (bSaveNotLoad)? zHere : zThere; sqlite3 *dbStore = 0; int openFlags = (bSaveNotLoad) ? SQLITE_OPEN_URI|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE : SQLITE_OPEN_READONLY; /* Ensure store DB can be opened and/or created appropriately. */ rc = sqlite3_open_v2(zStoreDbName, &dbStore, openFlags, 0); if( rc!=SQLITE_OK ){ utf8_printf(STD_ERR, "Error: Cannot %s parameter store DB %s\n", bSaveNotLoad? "open/create" : "read", zStoreDbName); return rc; } /* Ensure it has the parameter store table, or handle its absence. */ assert(dbStore!=0); if( sqlite3_table_column_metadata (dbStore, "main", PARAM_STORE_NAME, 0, 0, 0, 0, 0, 0)!=SQLITE_OK ){ if( !bSaveNotLoad ){ utf8_printf(STD_ERR, "Error: No parameters ever stored in DB %s\n", zStoreDbName); rc = 1; }else{ /* The saved parameters table is not there yet; create it. */ const char *zCT = "CREATE TABLE IF NOT EXISTS "PARAM_STORE_NAME"(\n" " key TEXT PRIMARY KEY,\n" " value,\n" " uses INT\n" ") WITHOUT ROWID;"; rc = sqlite3_exec(dbStore, zCT, 0, 0, 0); if( rc!=SQLITE_OK ){ utf8_printf(STD_ERR, "Cannot create table %s. Nothing saved.", zThere); } } } sqlite3_close(dbStore); if( rc!=0 ) return rc; zSql = smprintf("ATTACH %Q AS %s;", zStoreDbName, PARAM_STORE_SCHEMA); shell_check_oom(zSql); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ) return rc; sbCopy = sqlite3_str_new(db); sqlite3_str_appendf (sbCopy, "INSERT OR REPLACE INTO %s(key,value,uses)" "SELECT key, value, uses FROM %s WHERE key ", zTo, zFrom); append_in_clause(sbCopy, azNames, azNames+nNames); zSql = sqlite3_str_finish(sbCopy); shell_check_oom(zSql); rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); sqlite3_exec(db, "DETACH "PARAM_STORE_SCHEMA";", 0, 0, 0); return rc; } /* Default location of parameters store DB for .parameters save/load. */ static const char *zDefaultParamStore = "~/sqlite_params.sdb"; /* Possibly generate a derived path from input spec, with defaulting * and conversion of leading (or only) tilde as home directory. * The above-set default is used for zSpec NULL, "" or "~". * When return is 0, there is an error; what needs doing cannnot be done. * If the return is exactly the input, it must not be sqlite3_free()'ed. * If the return differs from the input, it must be sqlite3_free()'ed. */ static const char *params_store_path(const char *zSpec){ if( zSpec==0 || zSpec[0]==0 || strcmp(zSpec,"~")==0 ){ return home_based_path(zDefaultParamStore); }else if ( zSpec[0]=='~' ){ return home_based_path(zSpec); } return zSpec; } /* Load some or all parameters. Arguments are "load FILE ?NAMES?". */ static int parameters_load(sqlite3 *db, const char *azArg[], int nArg){ const char *zStore = params_store_path((nArg>1)? azArg[1] : 0); if( zStore==0 ){ utf8_printf(STD_ERR, "Cannot form parameter load path. Nothing loaded.\n"); return DCR_Error; }else{ const char **pzFirst = (nArg>2)? azArg+2 : 0; int nNames = (nArg>2)? nArg-2 : 0; int rc = param_xfr_table(db, zStore, 0, pzFirst, nNames); if( nArg>1 && zStore!=azArg[1] ) sqlite3_free((void*)zStore); return rc; } } /* Save some or all parameters. Arguments are "save FILE ?NAMES?". */ static int parameters_save(sqlite3 *db, const char *azArg[], int nArg){ const char *zStore = params_store_path((nArg>1)? azArg[1] : 0); if( zStore==0 ){ utf8_printf(STD_ERR, "Cannot form parameter save path. Nothing saved.\n"); return DCR_Error; }else{ const char **pzFirst = (nArg>2)? azArg+2 : 0; int nNames = (nArg>2)? nArg-2 : 0; int rc = param_xfr_table(db, zStore, 1, pzFirst, nNames); if( nArg>1 && zStore!=azArg[1] ) sqlite3_free((void*)zStore); return rc; } } #ifndef SQLITE_NOHAVE_SYSTEM /* * Edit one named parameter in the parameters table. If it does not * yet exist, create it. If eval is true, the value is treated as a * bare expression, otherwise it is a text value. The uses argument * sets the 3rd column in the parameters table, and may also serve * to partition the key namespace. (This is not done now.) */ static int edit_one_param(sqlite3 *db, char *name, int eval, ParamTableUse uses, const char * zEditor){ struct param_row paramVU = {0,0,0}; int rc; char * zVal = 0; char * zSql = smprintf ("SELECT value, uses FROM " PARAM_TABLE_SNAME " WHERE key=%Q", name); shell_check_oom(zSql); sqlite3_exec(db, zSql, param_find_callback, ¶mVU, 0); sqlite3_free(zSql); assert(paramVU.hits<2); if( paramVU.hits==1 && paramVU.uses==uses){ /* Editing an existing value of same kind. */ sqlite3_free(paramVU.value); if( eval!=0 ){ zSql = smprintf ("SELECT edit(value, %Q) FROM " PARAM_TABLE_SNAME " WHERE key=%Q AND uses=%d", zEditor, name, uses); zVal = db_text(db, zSql, 1); sqlite3_free(zSql); zSql = smprintf ("UPDATE "PARAM_TABLE_SNAME" SET value=(SELECT %s) WHERE" " key=%Q AND uses=%d", zVal, name, uses); }else{ zSql = smprintf ("UPDATE "PARAM_TABLE_SNAME" SET value=edit(value, %Q) WHERE" " key=%Q AND uses=%d", zEditor, name, uses); } }else{ /* Editing a new value of same kind. */ assert(paramVU.value==0 || paramVU.uses!=uses); if( eval!=0 ){ zSql = smprintf ("SELECT edit('-- %q%s', %Q)", name, "\n", zEditor); zVal = db_text(db, zSql, 1); sqlite3_free(zSql); zSql = smprintf ("INSERT INTO "PARAM_TABLE_SNAME"(key,value,uses)" " VALUES (%Q,(SELECT %s LIMIT 1),%d)", name, zVal, uses); }else{ zSql = smprintf ("INSERT INTO "PARAM_TABLE_SNAME"(key,value,uses)" " VALUES (%Q,edit('-- %q%s', %Q),%d)", name, name, "\n", zEditor, uses); } } shell_check_oom(zSql); if( eval!=0 ){ } rc = sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); sqlite3_free(zVal); return rc!=SQLITE_OK; } #endif /* Space-join values in an argument list. *valLim is not included. */ char *values_join( char **valBeg, char **valLim ){ char *z = 0; const char *zSep = 0; while( valBeg < valLim ){ z = smprintf("%z%s%s", z, zSep, *valBeg); zSep = " "; ++valBeg; } return z; } /* Get a named parameter value in form of stepped prepared statement, * ready to have its value taken from the 0th column. If the name * cannot be found for the given ParamTableUse, 0 is returned. * The caller is responsible for calling sqlite3_finalize(pStmt), * where pStmt is the return from this function. */ static sqlite3_stmt *get_param_value(sqlite3 *db, char *name, ParamTableUse ptu){ sqlite3_stmt *rv = 0; int rc; char *zSql = smprintf ( "SELECT value FROM "PARAM_TABLE_SNAME " WHERE key=%Q AND uses=%d", name, ptu ); shell_check_oom(zSql); rc = sqlite3_prepare_v2(db, zSql, -1, &rv, 0); sqlite3_free(zSql); if( SQLITE_OK==rc ){ if( SQLITE_ROW==sqlite3_step(rv) ) return rv; sqlite3_finalize(rv); } return 0; } static struct ParamSetOpts { const char cCast; const char *zTypename; int evalKind; } param_set_opts[] = { /* { 'q', 0, 2 }, */ |
︙ | ︙ | |||
10757 10758 10759 10760 10761 10762 10763 | ++zArg; if( zArg[0]=='-' ) ++zArg; if( zArg[0]!=0 && zArg[1]==0 ) return zArg[0]; } return 0; } | < < < < < < < < < < < < < < < < < < < < < < | | > | < | | | | | > | < < < < < < < < < < < < < < < | < | | 10634 10635 10636 10637 10638 10639 10640 10641 10642 10643 10644 10645 10646 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 10657 10658 10659 10660 10661 10662 10663 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 | ++zArg; if( zArg[0]=='-' ) ++zArg; if( zArg[0]!=0 && zArg[1]==0 ) return zArg[0]; } return 0; } /* The set subcommand (per help text) */ static int param_set(sqlite3 *db, char cCast, char *name, char **valBeg, char **valLim, ParamTableUse ptu){ char *zSql = 0; int rc = SQLITE_OK, retries = 0, needsEval = 1; char *zValGlom = (valLim-valBeg>1)? values_join(valBeg, valLim) : 0; sqlite3_stmt *pStmtSet = 0; const char *zCastTo = 0; char *zValue = (zValGlom==0)? *valBeg : zValGlom; if( cCast ){ struct ParamSetOpts *pSO = param_set_opts; for(; pSO-param_set_opts < ArraySize(param_set_opts); ++pSO ){ if( cCast==pSO->cCast ){ zCastTo = pSO->zTypename; needsEval = pSO->evalKind > 0; break; } } } if( needsEval ){ if( zCastTo!=0 ){ zSql = smprintf ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)" " VALUES(%Q,CAST((%s) AS %s),%d);", name, zValue, zCastTo, ptu ); }else{ zSql = smprintf ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)" "VALUES(%Q,(%s),%d);", name, zValue, ptu ); } shell_check_oom(zSql); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmtSet, 0); sqlite3_free(zSql); } if( !needsEval || rc!=SQLITE_OK ){ /* Reach here when value either requested to be cast to text, or must be. */ sqlite3_finalize(pStmtSet); pStmtSet = 0; zSql = smprintf ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)" "VALUES(%Q,%Q,%d);", name, zValue, ptu ); shell_check_oom(zSql); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmtSet, 0); assert(rc==SQLITE_OK); sqlite3_free(zSql); } sqlite3_step(pStmtSet); sqlite3_finalize(pStmtSet); sqlite3_free(zValGlom); return rc; } /* list or ls subcommand for .parameter dot-command */ static void list_params(ShellExState *psx, ParamTableUse ptu, u8 bShort, char **pzArgs, int nArg){ sqlite3_stmt *pStmt = 0; sqlite3 *db = DBX(psx); sqlite3_str *sbList = sqlite3_str_new(db); int len = 0, rc; char *zFromWhere = 0; char *zSql = 0; sqlite3_str_appendf(sbList, "FROM "PARAM_TABLE_SNAME " WHERE (?1=3 OR uses=?1) AND "); append_glob_terms(sbList, "key", (const char **)pzArgs, (const char **)pzArgs+nArg); zFromWhere = sqlite3_str_finish(sbList); shell_check_oom(zFromWhere); zSql = smprintf("SELECT max(length(key)) %s", zFromWhere); shell_check_oom(zSql); rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
︙ | ︙ | |||
10979 10980 10981 10982 10983 10984 10985 | ".parameter CMD ... Manage SQL parameter bindings and scripts table", " clear ?NAMES? Erase all or only given named parameters", #ifndef SQLITE_NOHAVE_SYSTEM " edit ?OPT? NAME ... Use edit() to create or alter parameter NAME", " OPT may be -t to use edited value as text or -e to evaluate it.", #endif " init Initialize TEMP table for bindings and scripts", | | | > | | | 10819 10820 10821 10822 10823 10824 10825 10826 10827 10828 10829 10830 10831 10832 10833 10834 10835 10836 10837 10838 10839 10840 10841 10842 10843 | ".parameter CMD ... Manage SQL parameter bindings and scripts table", " clear ?NAMES? Erase all or only given named parameters", #ifndef SQLITE_NOHAVE_SYSTEM " edit ?OPT? NAME ... Use edit() to create or alter parameter NAME", " OPT may be -t to use edited value as text or -e to evaluate it.", #endif " init Initialize TEMP table for bindings and scripts", " list ?PATTERNS? List parameters table binding and script values", " Alternatively, to list just some or all names: ls ?PATTERNS?", " load ?FILE? ?NAMES? Load some or all named parameters from FILE", " If FILE missing, empty or '~', it defaults to ~/sqlite_params.sdb", " save ?FILE? ?NAMES? Save some or all named parameters into FILE", " If FILE missing, empty or '~', it defaults to ~/sqlite_params.sdb", " set ?TOPT? NAME VALUE Give SQL parameter NAME a value of VALUE", " NAME must begin with one of $,:,@,? for bindings, or with a letter", " to be executable; value is the space-joined argument list.", " Option TOPT may be one of {-b -i -n -r -t} to cast effective value", " to BLOB, INT, NUMERIC, REAL or TEXT respectively.", " unset ?NAMES? Remove named parameter(s) from parameters table", ]; DISPATCHABLE_COMMAND( parameter 2 2 0 ){ int rc = 0; open_db(p,0); sqlite3 *db = DBX(p); |
︙ | ︙ | |||
11017 11018 11019 11020 11021 11022 11023 11024 11025 11026 11027 11028 | sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); } }else #ifndef SQLITE_NOHAVE_SYSTEM /* .parameter edit ?NAMES? ** Edit the named parameters. Any that do not exist are created. */ if( strcmp(azArg[1],"edit")==0 ){ ShellInState *psi = ISS(p); int ia = 2; int eval = 0; | > < < > > > > > | > > > > > > > > > > > > > | < > < | | | > | | | | | | | | | | | > | | 10858 10859 10860 10861 10862 10863 10864 10865 10866 10867 10868 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 10992 10993 10994 10995 10996 10997 10998 | sqlite3_exec(db, zSql, 0, 0, 0); sqlite3_free(zSql); } }else #ifndef SQLITE_NOHAVE_SYSTEM /* .parameter edit ?NAMES? ** Edit the named parameters. Any that do not exist are created. ** New ones get a uses tag auto-selected by their leading char. */ if( strcmp(azArg[1],"edit")==0 ){ ShellInState *psi = ISS(p); int ia = 2; int eval = 0; if( !INSOURCE_IS_INTERACTIVE(psi->pInSource) ){ utf8_printf(STD_ERR, "Error: " ".parameter edit can only be used interactively.\n"); return DCR_Error; } param_table_init(db); if( psi->zEditor==0 ){ const char *zE = getenv("VISUAL"); if( zE!=0 ) psi->zEditor = smprintf("%s", zE); } if( nArg>=3 && azArg[2][0]=='-' ){ char *zArg = (azArg[2][1]=='-')? azArg[2]+2 : azArg[2]+1; if( strncmp(zArg,"editor=",7)==0 ){ sqlite3_free(psi->zEditor); /* Accept an initial -editor=? option. */ psi->zEditor = smprintf("%s", zArg+7); ++ia; } } if( psi->zEditor==0 ){ utf8_printf(STD_ERR, "Either set env-var VISUAL to name an" " editor and restart, or rerun\n " ".parameter edit with an initial " "edit option, --editor=EDITOR_COMMAND .\n"); return DCR_Error; } /* Future: Allow an option whereby new value can be evaluated * the way that .parameter set ... does. */ while( ia < nArg ){ ParamTableUse ptu; char cf = (azArg[ia][0]=='-')? azArg[ia][1] : 0; if( cf!=0 && azArg[ia][2]==0 ){ ++ia; switch( cf ){ case 'e': eval = 1; continue; case 't': eval = 0; continue; default: utf8_printf(STD_ERR, "Error: bad .parameter name: %s\n", azArg[--ia]); return DCR_Error; } } ptu = classify_param_name(azArg[ia]); if( ptu==PTU_Nil ){ utf8_printf(STD_ERR, "Error: %s cannot be a binding or executable" " parameter name.\n", azArg[ia]); return DCR_Error; } rc = edit_one_param(db, azArg[ia], eval, ptu, psi->zEditor); ++ia; if( rc!=0 ) return rc; } }else #endif /* .parameter init ** Make sure the TEMP table used to hold bind parameters exists. ** Create it if necessary. */ if( nArg==2 && strcmp(azArg[1],"init")==0 ){ param_table_init(db); }else /* .parameter list|ls ** List all or selected bind parameters. ** list displays names, values and uses. ** ls displays just the names. */ if( nArg>=2 && ((strcmp(azArg[1],"list")==0) || (strcmp(azArg[1],"ls")==0)) ){ list_params(p, PTU_Nil, azArg[1][1]=='s', azArg+2, nArg-2); }else /* .parameter load ** Load all or named parameters from specified or default (DB) file. */ if( strcmp(azArg[1],"load")==0 ){ param_table_init(db); rc = parameters_load(db, (const char **)azArg+1, nArg-1); }else /* .parameter save ** Save all or named parameters into specified or default (DB) file. */ if( strcmp(azArg[1],"save")==0 ){ rc = parameters_save(db, (const char **)azArg+1, nArg-1); }else /* .parameter set NAME VALUE ** Set or reset a bind parameter. NAME should be the full parameter ** name exactly as it appears in the query. (ex: $abc, @def). The ** VALUE can be in either SQL literal notation, or if not it will be ** understood to be a text string. */ if( nArg>=4 && strcmp(azArg[1],"set")==0 ){ char cCast = option_char(azArg[2]); int inv = 2 + (cCast != 0); ParamTableUse ptu = classify_param_name(azArg[inv]); if( ptu==PTU_Nil ){ utf8_printf(STD_ERR, "Error: %s is not a usable parameter name.\n", azArg[inv]); rc = 1; }else{ param_table_init(db); rc = param_set(db, cCast, azArg[inv], &azArg[inv+1], &azArg[nArg], ptu); if( rc!=SQLITE_OK ){ utf8_printf(STD_ERR, "Error: %s\n", sqlite3_errmsg(db)); rc = 1; } } }else { /* If no command name and arg count matches, show a syntax error */ showHelp(ISS(p)->out, "parameter", p); return DCR_CmdErred; } return rc; } /***************** * The .print, .progress and .prompt commands */ CONDITION_COMMAND( progress !defined(SQLITE_OMIT_PROGRESS_CALLBACK) ); COLLECT_HELP_TEXT[ |
︙ | ︙ | |||
13013 13014 13015 13016 13017 13018 13019 | } }else{ goto teach_fail; } return DCR_Ok; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 12872 12873 12874 12875 12876 12877 12878 12879 12880 12881 12882 12883 12884 12885 | } }else{ goto teach_fail; } return DCR_Ok; } /***************** * The .vfsinfo, .vfslist, .vfsname and .version commands */ COLLECT_HELP_TEXT[ ".version Show a variety of version info", ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", |
︙ | ︙ | |||
13378 13379 13380 13381 13382 13383 13384 | } return rc; } DISPATCHABLE_COMMAND( x ? 1 0 ){ int ia, nErrors = 0; sqlite3_stmt *pStmt = 0; | | | | > > | | > > > > > | | | | 13108 13109 13110 13111 13112 13113 13114 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 | } return rc; } DISPATCHABLE_COMMAND( x ? 1 0 ){ int ia, nErrors = 0; sqlite3_stmt *pStmt = 0; sqlite3 *db = 0; DotCmdRC rv = DCR_Ok; enum { AsParam, AsString, AsFile } evalAs = AsParam; for( ia=1; ia < nArg; ++ia ){ char *zSubmit = 0; const char *zOpt = azArg[ia]; if ( *zOpt == '-' ){ static const char *azOpts[] = { "p", "s", "f" }; int io = ArraySize(azOpts); while( io > 0 ){ if( optionMatch(zOpt, azOpts[--io]) ){ evalAs = io; zOpt = 0; break; } } if( zOpt==0 ) continue; } switch( evalAs ){ case AsParam: if( pStmt==0 ){ int rc; open_db(p, 0); db = DBX(p); if( db==0 ){ utf8_printf(STD_ERR, ".x can only be done with a database open.\n"); return DCR_Error; } if( sqlite3_table_column_metadata(db, PARAM_TABLE_SCHEMA, PARAM_TABLE_NAME, "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){ utf8_printf(STD_ERR, "No "PARAM_TABLE_SNAME" table exists.\n"); return DCR_Error; } rc = sqlite3_prepare_v2(db, "SELECT value FROM "PARAM_TABLE_SNAME " WHERE key=$1 AND uses=1", -1, &pStmt, 0); if( rc!=SQLITE_OK ){ utf8_printf(STD_ERR, PARAM_TABLE_SNAME" is wrongly created.\n"); return DCR_Error; } } if( isalpha(azArg[ia][0]) ){ int rc = sqlite3_reset(pStmt); rc = sqlite3_bind_text(pStmt, 1, azArg[ia], -1, 0); rc = sqlite3_step(pStmt); |
︙ | ︙ | |||
13436 13437 13438 13439 13440 13441 13442 | if( rv>DCR_Error ) break; } }else{ continue; /* All white (or OOM), ignore. */ } }else{ utf8_printf(STD_ERR, | | | 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 | if( rv>DCR_Error ) break; } }else{ continue; /* All white (or OOM), ignore. */ } }else{ utf8_printf(STD_ERR, "Skipping parameter '%s' (not set and executable.)\n", azArg[ia]); ++nErrors; } }else{ utf8_printf(STD_ERR, "Skipping badly named %s. Run \".help x\"\n", azArg[ia]); ++nErrors; |
︙ | ︙ | |||
13596 13597 13598 13599 13600 13601 13602 | } MetaMatchIter; /* Prepare an iterator that will produce a sequence of MetaCommand * pointers whose referents names match the given cmdFragment. */ static MetaMatchIter findMatchingMetaCmds(const char *cmdFragment, ShellExState *psx){ MetaMatchIter rv = { psx, 0, 0 }; | | | | 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 | } MetaMatchIter; /* Prepare an iterator that will produce a sequence of MetaCommand * pointers whose referents names match the given cmdFragment. */ static MetaMatchIter findMatchingMetaCmds(const char *cmdFragment, ShellExState *psx){ MetaMatchIter rv = { psx, 0, 0 }; if( psx->dbShell==0 ){ rv.zPattern = smprintf("%s*", cmdFragment? cmdFragment : ""); shell_check_oom((void *)rv.zPattern); rv.pMC = (MetaCommand *)command_table; }else{ /* Prepare rv.stmt to yield results glob-matching cmdFragment. */ const char *zSql = "SELECT name, extIx, cmdIx FROM ActiveCommands " "WHERE name glob (?||'*') ORDER BY name"; sqlite3_prepare_v2(psx->dbShell, zSql, -1, &rv.stmt, 0); sqlite3_bind_text(rv.stmt, 1, cmdFragment? cmdFragment : "", -1, 0); } return rv; } /* Produce the next MetaCommand pointer from the iterator, or 0 if no next. */ |
︙ | ︙ | |||
13664 13665 13666 13667 13668 13669 13670 | ** If shell extensions are loaded, the match must be long enough to ** result in a unique lookup. */ MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx, /* out */ int *pnFound){ if( pnFound ) *pnFound = 0; #if SHELL_DYNAMIC_EXTENSION | | | | 13401 13402 13403 13404 13405 13406 13407 13408 13409 13410 13411 13412 13413 13414 13415 13416 13417 13418 13419 13420 | ** If shell extensions are loaded, the match must be long enough to ** result in a unique lookup. */ MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx, /* out */ int *pnFound){ if( pnFound ) *pnFound = 0; #if SHELL_DYNAMIC_EXTENSION if( psx->dbShell!=0 ){ int rc; int extIx = -1, cmdIx = -1, nf = 0; sqlite3_stmt *pStmt = 0; const char *zSql = "SELECT COUNT(*), extIx, cmdIx" " FROM ActiveCommands WHERE name glob (?||'*')"; rc = sqlite3_prepare_v2(psx->dbShell, zSql, -1, &pStmt, 0); sqlite3_bind_text(pStmt, 1, cmdName, -1, 0); rc = sqlite3_step(pStmt); nf = sqlite3_column_int(pStmt, 0); extIx = sqlite3_column_int(pStmt, 1); cmdIx = sqlite3_column_int(pStmt, 2); sqlite3_finalize(pStmt); |
︙ | ︙ | |||
14636 14637 14638 14639 14640 14641 14642 | } /* end glom another line */ } /* end group collection loop */ /* Here, the group is fully collected or known to be incomplete forever. */ switch( disposition ){ case Dumpable: echo_group_input(psi, *pzLineUse); #if SHELL_DYNAMIC_EXTENSION | | | 14373 14374 14375 14376 14377 14378 14379 14380 14381 14382 14383 14384 14385 14386 14387 | } /* end glom another line */ } /* end group collection loop */ /* Here, the group is fully collected or known to be incomplete forever. */ switch( disposition ){ case Dumpable: echo_group_input(psi, *pzLineUse); #if SHELL_DYNAMIC_EXTENSION if( inKind==Script && pSS!=0 ) pSS->pMethods->restartCompletionScan(pSS); #endif break; case Runnable: switch( inKind ){ case Sql: /* runOneSqlLine() does its own echo when requested. */ nErrors += runOneSqlLine(XSS(psi), *pzLineUse, |
︙ | ︙ | |||
14661 14662 14663 14664 14665 14666 14667 | break; } #if SHELL_DYNAMIC_EXTENSION case Script: { char *zErr = 0; DotCmdRC dcr; assert(pSS!=0); | | | 14398 14399 14400 14401 14402 14403 14404 14405 14406 14407 14408 14409 14410 14411 14412 | break; } #if SHELL_DYNAMIC_EXTENSION case Script: { char *zErr = 0; DotCmdRC dcr; assert(pSS!=0); pSS->pMethods->restartCompletionScan(pSS); dcr = pSS->pMethods->runScript(pSS, *pzLineUse+nLeadWhite, XSS(psi), &zErr); if( dcr!=DCR_Ok || zErr!=0 ){ /* ToDo: Handle errors more informatively and like dot commands. */ nErrors += (dcr!=DCR_Ok); if( zErr!=0 ){ utf8_printf(STD_ERR, "Error: %s\n", zErr); |
︙ | ︙ | |||
14687 14688 14689 14690 14691 14692 14693 | termKind = DCR_Exit; } break; case Erroneous: utf8_printf(STD_ERR, "Error: Input incomplete at line %d of \"%s\"\n", psi->pInSource->lineno, psi->pInSource->zSourceSay); #if SHELL_DYNAMIC_EXTENSION | | | 14424 14425 14426 14427 14428 14429 14430 14431 14432 14433 14434 14435 14436 14437 14438 | termKind = DCR_Exit; } break; case Erroneous: utf8_printf(STD_ERR, "Error: Input incomplete at line %d of \"%s\"\n", psi->pInSource->lineno, psi->pInSource->zSourceSay); #if SHELL_DYNAMIC_EXTENSION if( inKind==Script && pSS!=0 ) pSS->pMethods->restartCompletionScan(pSS); #endif ++nErrors; break; case Ignore: break; default: assert(0); } |
︙ | ︙ |
Changes to src/shext_linkage.h.
︙ | ︙ | |||
274 275 276 277 278 279 280 | DECORATE_METHOD(Derived,help), \ DECORATE_METHOD(Derived,argsCheck), \ DECORATE_METHOD(Derived,execute) \ } /* This function pointer has the same signature as the sqlite3_X_init() * function that is called as SQLite3 completes loading an extension. | | | 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | DECORATE_METHOD(Derived,help), \ DECORATE_METHOD(Derived,argsCheck), \ DECORATE_METHOD(Derived,execute) \ } /* This function pointer has the same signature as the sqlite3_X_init() * function that is called as SQLite3 completes loading an extension. * It is used as a process-unique identifier for a loaded extention. */ typedef int (*ExtensionId) (sqlite3 *, char **, const struct sqlite3_api_routines *); typedef struct Prompts { const char *zMain; const char *zContinue; |
︙ | ︙ |
Changes to tool/mkshellc.tcl.
︙ | ︙ | |||
73 74 75 76 77 78 79 | set ::topDir [file dir [file dir [file normal $argv0]]] set runMode normal set ::lineTags 0 ; # 0 => none, 1 => source change, 2 => line syncs, 3 => more set ::tclGenerate 0 | < | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | set ::topDir [file dir [file dir [file normal $argv0]]] set runMode normal set ::lineTags 0 ; # 0 => none, 1 => source change, 2 => line syncs, 3 => more set ::tclGenerate 0 set ::verbosity 0 set ::inFiles {} set ::topInfile "?" set ::presumedOutfile "?" set ::targetProgram "?" set ::defaultInfile "src/shell.c.in" array set ::incTypes [list "*" "$::topDir/$::defaultInfile"] |
︙ | ︙ | |||
364 365 366 367 368 369 370 | COMMENT {\s+(.*)$} \ CONDITION_COMMAND {^\(\s*(\w+)\s+([^;]+)\);} \ DISPATCH_CONFIG {^\[} \ DISPATCHABLE_COMMAND {^\(([\w\? ]+)\)(\S)\s*$} \ EMIT_METACMD_INIT {^\((\d*)\)} \ INCLUDE {^(?:\(\s*(\w+)\s*\))|(?:\s+([\w./\\]+)\M)} \ IGNORE_COMMANDS {^\(\s*([-+\w ]*)\)\s*;\s*} \ | < | < < | | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | COMMENT {\s+(.*)$} \ CONDITION_COMMAND {^\(\s*(\w+)\s+([^;]+)\);} \ DISPATCH_CONFIG {^\[} \ DISPATCHABLE_COMMAND {^\(([\w\? ]+)\)(\S)\s*$} \ EMIT_METACMD_INIT {^\((\d*)\)} \ INCLUDE {^(?:\(\s*(\w+)\s*\))|(?:\s+([\w./\\]+)\M)} \ IGNORE_COMMANDS {^\(\s*([-+\w ]*)\)\s*;\s*} \ ] # Names of the subcaptures as formal parameter to macro procs. # COMMENT tailCapture_Commentary # CONDITION_COMMAND tailCapture_Cmd_Condition # CONFIGURE_DISPATCH tailCapture_Empty # COLLECT_HELP_TEXT tailCapture_Empty # DISPATCHABLE_COMMAND tailCapture_ArgsGlom_TrailChar # EMIT_METACMD_INIT tailCapture_Indent # IGNORED_COMMANDS tailCapture_SignedCmdGlom # INCLUDE tailCapture_IncType_Filename array set ::macroUsages [list \ COLLECT_HELP_TEXT "\[\n <help text lines>\n \];" \ COMMENT " <arbitrary characters to end of line>" \ CONDITION_COMMAND "( name pp_expr );" \ DISPATCH_CONFIG "\[\n <NAME=value lines>\n \];" \ DISPATCHABLE_COMMAND \ "( name args... ){\n <implementation code lines>\n }" \ EMIT_METACMD_INIT "( indent );" \ INCLUDE {( <inc_type> )} \ SKIP_COMMANDS "( <signed_names> );" \ ] # RE for early discard of non-macro lines, matching all above keywords set ::macroKeywordTailRE \ {^\s{0,8}((?:(?:CO)|(?:DI)|(?:EM)|(?:IN)|(?:SK))[A-Z_]+)\M(.+)$} ######## # Macro procs, general signature and usage: # inSrc is a triple, { input_filename open_input_stream input_lines_consumed }. # Arg 2 is the macro tail as RE-captured by one of ::macroTailREs . # ostrm is the open output stream for all regular output. # The number of input lines consumed, including macro invocation, is returned. |
︙ | ︙ | |||
508 509 510 511 512 513 514 | if {[regexp {^\s*(\w+)=(.+)$} $line ma k v]} { set ::dispCfg($k) $v } } return $iAte } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | if {[regexp {^\s*(\w+)=(.+)$} $line ma k v]} { set ::dispCfg($k) $v } } return $iAte } proc DISPATCHABLE_COMMAND {inSrc tailCapture ostrm} { # Generate and emit a function definition, maybe wrapped as set by # CONDITION_COMMAND(), and generate/collect its dispatch table entry, # as determined by its actual arguments and DISPATCH_CONFIG parameters. foreach { srcFile istrm srcPrecLines } $inSrc break set args [lindex $tailCapture 0] set tc [lindex $tailCapture 1] |
︙ | ︙ | |||
674 675 676 677 678 679 680 | # This function may consume additional lines via triple inSrc. proc do_macro {inSrc lx ostrm} { if {![regexp $::macroKeywordTailRE $lx ma macro tail] \ || ![info exists ::macroTailREs($macro)]} { return 0 } # It's an attempted macro invocation line. Process or fail and yap. | < < < | | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 | # This function may consume additional lines via triple inSrc. proc do_macro {inSrc lx ostrm} { if {![regexp $::macroKeywordTailRE $lx ma macro tail] \ || ![info exists ::macroTailREs($macro)]} { return 0 } # It's an attempted macro invocation line. Process or fail and yap. set tailCap [regexp -inline $::macroTailREs($macro) $tail] # Call like-named proc with any args captured by the corresponding RE. return [$macro $inSrc [lrange $tailCap 1 end] $ostrm] } array set ::typedefsSeen {} array set ::includesDone {} # Filter redundant typedefs and certain includes and qualifiers, in place. |
︙ | ︙ |