亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Pro*C介紹-內嵌SQL

系統 1781 0

概要

內嵌SQL是結合高級語言如C/C++的計算能力和SQL數據庫處理能力的一種方法。它允許你在程序中執行任意的SQL語句。Oracle的嵌入SQL環境稱為Pro*C。

Pro*C程序分兩步編譯。首先,Pro*C的預編譯器識別出嵌入在程序中的SQL語句,并將這些語句轉換為對SQL運行時庫(SQL runtime library)中功能(functions)的適當調用。輸出是純C/C++代碼和未被處理的純C/C++代碼。然后,用常規C/C++編譯器編譯代碼并生成可執行程序。更詳細的內容請參考 Demo程序 。

Demo程序 。

Pro*C語法


SQL

所有SQL語句都要以 EXEC SQL 開始,并用分號" ; "結束。SQL語句可以放置在C/C++塊中的任何地方,但可執行的(SQL)語句應該在聲明語句后面。例:

      {

    int a;

    /* ... */

    EXEC SQL SELECT salary INTO :a

             FROM Employee

             WHERE SSN=876543210;

    /* ... */

    printf("The salary is %d\n", a);

    /* ... */

}
    


預處理指令

能夠在Pro*C中正常工作的C/C++預處理指令是 #include #if 。Pro*C不能識別 #define 。下面的代碼是錯誤的:

      #define THE_SSN 876543210

/* ... */

EXEC SQL SELECT salary INTO :a

         FROM Employee

         WHERE SSN = THE_SSN;/* INVALID */
    

語句標號

可以在SQL中跳轉到C/C++標記

          EXEC SQL WHENEVER SQLERROR GOTO error_in_SQL;

    /* ... */

error_in_SQL:

    /* do error handling */
    

我們會在后面的 錯誤處理 一節中講到有關 WHENEVER 的含意。

錯誤處理 一節中講到有關 WHENEVER 的含意。

宿主變量


基礎

宿主變量是連接宿主程序與數據庫的關鍵。宿主變量表達式必須視為(resolve to)左值(能被賦值)。你可以像聲明普通C變量一樣,按著C的語法規則聲明宿主變量。宿主變量的聲明可以放置在任何C變量聲明可以放置的地方。(C++用戶需要使用" DECLARE SECTION ";參考 C++ Users ) Oracle可以使用的C數據類型包括:

C++ Users ) Oracle可以使用的C數據類型包括:
  • char
  • char[ n ]
  • int
  • short
  • long
  • float
  • double
  • VARCHAR[ n ] - 它是能被Pro*C的預編譯器識別的預處理類型(psuedo-type)。它用來 表示由空白填充(blank-padded,譯注:' \0 ')的變長字符串。Pro*C預編譯器會把它轉換為有一個2字節(byte)長的域和一個n字(byte)長的字符數組的結構體。

你不能指定寄存器存儲類型(譯注:指針)為宿主變量。

可以在SQL表達式中使用冒號" : "做前綴來引用一個宿主變量,但不能在C表達式中用分號做前綴。當使用字符串作為宿主變量時,必須省略引用;Pro*C明白你正指定一個基于宿主變量聲明類型的字符串(譯注:這句的意思是,當定義一個字符串做為宿主變量時 char *str="string" ,在嵌入SQL中使用時,要省略" * "而不是 *str )。不能將C函數調用和多數的指針計算表達式作為宿主變量使用,即使它們確實被解釋為左值。下面的代碼同時說明了合法和不合法的宿主變量的引用:

    int deptnos[3] = { 000, 111, 222 };

int get_deptno() { return deptnos[2]; }

int *get_deptnoptr() { return &(deptnos[2]); }

int main() 

{

    int x; char *y; int z;

    /* ... */

    EXEC SQL INSERT INTO emp(empno, ename, deptno)

        VALUES(:x, :y, :z); /* LEGAL */

    EXEC SQL INSERT INTO emp(empno, ename, deptno)

        VALUES(:x + 1,  /* LEGAL: the reference is to x */

               'Big Shot',  /* LEGAL: but not really a host var */

               :deptnos[2]);/* LEGAL: array element is fine */

    EXEC SQL INSERT INTO emp(empno, ename, deptno)

        VALUES(:x, :y,

               :(*(deptnos+2)));/* ILLEGAL: although it has anlvalue */

    EXEC SQL INSERT INTO emp(empno, ename, deptno)

        VALUES(:x, :y,

               :get_deptno());  /* ILLEGAL: no function calls */

    EXEC SQL INSERT INTO emp(empno, ename, deptno)

        VALUES(:x, :y,

               :(*get_depnoptr())); /* ILLEGAL: although it has an lvalue */

    /* ... */

}
  


指針

可以在SQL表達式中使用普通C語法聲明的指針。通常使用一個冒號做前綴:

      int*x;

/*...*/

EXEC SQL SELECT xyz INTO :x FROM ...;
    

這個 SELECT 語句的結果會寫入 *x ,而不是 x 。


結構

結構同樣可以作為宿主變量使用,如下面例子:

      typedef struct {

    char name[21];/* one greater than column length; for '\0' */

    int SSN;

} Emp;

/* ... */

Emp bigshot;

/* ... */

EXEC SQL INSERT INTO emp (ename, eSSN)

    VALUES (:bigshot);
    


數組

像下面這樣使用宿主數組:

      int emp_number[50];

char name[50][11];

/* ... */

EXEC SQL INSERT INTO emp(emp_number, name)

    VALUES (:emp_number, :emp_name);
    

這樣會一次插入所有的50個元素(tuples)。

數組只能是一維數組。例子中的 char name[50][11] 可能看起來與這個規則矛盾,然而,Pro*C實際上把 name 視為一維字符串數組而不是二維字符數組。也可以使用結構數組。

當使用數組存放查詢結果時,如果宿主數組的大小( n )小于實際查詢后返回的結果數量時,那么只有(查詢出來的)前 n 個結果被填入宿主數組。


指示器變量

指示器實際上是附在宿主變量后的" NULL 標記"。每一個宿主變量都可以選擇性的關聯到一個指示器變量上。指示器變量的類型必須為2字節整形值( short 類型),而且必須緊隨在宿主變量后且用冒號做為前綴。你也可以使用關鍵字 INDICATOR 放在宿主變量和指示器變量之間。例如:

      short indicator_var;

EXEC SQL SELECT xyz INTO :host_var:indicator_var

    FROM ...;

/* ... */

EXEC SQL INSERT INTO R

    VALUES(:host_var INDICATOR :indicator_var, ...);
    

SELECT INTO 子句中使用的指示器變量可以用來檢查返回給宿主變量的值為空(NULL)或被截斷的的情況。Oracle能夠賦給指示器變量的值具有下面各項意義:

-1 字段(譯注:原文為column,意為該字段的列)值為 NULL ,宿主變量的值不確定。
0 Oracle把完整的字值賦給了宿主變量。
>0 Oracle把被截斷的字段值賦給了宿主變量。指示器變量返回的整形值是字段的原始長度。
-2 Oracle把被截斷的字段值賦給了宿主變量,但原字段值是不確定的。(譯注:這種情況可能是數值型的值被切斷后不能確定原始值)

你也可以在 INSERT UPDATE VALUES SET 子句中使用指示器變量指明用于輸入的宿主變量為 NULL 值。你的程序可以賦給指示器變量的值具有如下各項意義:

-1 Oracle會忽略宿主變量的值并給字段賦一個 NULL 值。
>=0 Oracle會將宿主變量的值賦給字段。


數據類型同等化

Oracle認識兩種數據類型:內部類型和外部類型。內部數據類型指示Oracle在數據庫表中如何存儲字段。外部數據類型指示如何格式化存儲在宿主變量中用于輸入或輸出的值。在預編譯期,每一個宿主變量會被賦予一個默認的Oracle外部數據類型。數據類型同等化允許你重載默認的同等化,并允許你控制Oracle輸入數據的解釋和輸出數據的格式化。

同等化通過使用 VAR 表達式實現基于從變量到變量的轉換。語法是:

      EXEC SQL VAR <host_var> IS <type_name> [ (<length>) ];
    

舉例來說, 假設你想從 emp 表中查詢雇員名子,然后傳給一個需要C類型(以' \0 '結尾的)字符串的程序,你不需要顯式的用' \0 '來結束這個名子。如下面所示,簡單地將宿主變量同等化為外部數據類型 STRING

      char emp_name[21];

EXEC SQL VAR emp_name IS STRING(21);
    

emp 表中 ename 字段的長是20字符(character),因此,需要分配21字符(character)以適應' \0 '結束符。Oracle的外部數據類型 STRING 是為C類型字符串特別設計的接口。當把 ename 字段值傳給 emp_name 時,Oracle會自動用' \n '結尾。

也可以使用 TYPE 語句將用戶自定義數據類型同等化為Oracle外部數據類型。語法是:

      EXEC SQL TYPE <user_type> IS <type_name> [ (<length>) ] [REFERENCE];
    

可以聲明一個用戶自定義類型的指針,顯式的如指向標量或結構的指針,或隱式的如數組,然后在 TYPE 表達式中使用這個類型。這種情況下,你需要在表達式后使用 REFERENCE 子句,如下所示:

      typedef unsigned char *my_raw;

EXEC SQL TYPE my_raw IS VARRAW(4000) REFERENCE;

my_raw buffer;

/* ... */

buffer = malloc(4004);
    

這里,我們分配了比源類型長(4000)更多的內存,這是因為預編譯器要返回長度,而且可能需要填充適當的長度以滿足系統的對齊要求。


動態SQL

嵌入SQL能夠滿足一個固定的應用,但有時動態產生完整的SQL語句也是很重要的。對于動態SQL,語句存儲在字符串變量中, PREPARE 把字符串轉換為SQL語句,然后用 EXECUTE 執行這個語句??紤]下面的例子:

      char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";

EXEC SQL PREPARE q FROM :s;

EXEC SQL EXECUTE q;
    

PREPARE EXECUTE 可放到一個語句中,像這樣:

      char *s = "INSERT INTO emp VALUES(1234, 'jon', 3)";

EXEC SQL EXECUTE IMMEDIATE :s;
    


事務

Oracle Pro*C支持標準SQL定義的事務。一個事務是一組SQL語句集合,Oracle把它當作單獨的單元運行。一個事務從第一個SQL語句開始,遇到" EXEC SQL COMMIT "(執行當前事務對數據庫的永久修改)或" EXEC SQL ROLLBACK "(取消從事務開始到當前位置對數據庫的任何修改)時結束事務。當前事務由 COMMIT ROLLBACK 語句結束后,下一條可執行SQL語句將自動開始一個新事務。

如果程序結束時沒有執行 EXEC SQL COMMIT ,則對數據庫的所作的修改都將被忽略。


錯誤處理

在每個可執行的SQL表達式之后,可以在程序中檢查 SQLCA 顯式地或用 WHENEVER 語句隱式地得到執行狀態信息。下面將詳細地介紹這兩種方法。


SQLCA

SQLCA(SQL Communications Area,SQL通訊區)用于在程序中檢查錯誤和狀態變化。每個可執行SQL語句執行后,Oracle 運行時會在這個結構中填入信息。

如果要使用SQLCA,你要用 #include 包含進 sqlca.h 頭文件。如果在很多地方包含了頭文件,你要使用 #undef SQLCA 來取消 SQLCA 的宏定義。 sqlca.h 中的相關程序塊如下:

      #ifndef SQLCA

#define SQLCA 1



struct sqlca {

    /* ub1 */ char sqlcaid[8];

    /* b4 */ long sqlabc;

    /* b4 */ long sqlcode;

    struct {

        /* ub2 */ unsigned short sqlerrml;

        /* ub1 */ char sqlerrmc[70];

    } sqlerrm;

    /* ub1 */ char sqlerrp[8];

    /* b4 */ long sqlerrd[6];

    /* ub1 */ char sqlwarn[8];

    /* ub1 */ char sqlext[8];

};

/* ... */
    

sqlca域有下列的意義:

sqlcaid 該字符串域初始化為"SQLCA",以指明這是個SQL通訊區。
sqlcabc 該整數域用byte標識SQLCA結構的長度。
sqlcode 這個整數域標識最近執行的SQL語句的狀態碼:
?
0 沒有錯誤。
>0 語句執行但捕獲異常。當Oracle根據 WHERE 條件查找不到任何記錄,或 SELECT INTO FETCH 影響記錄數為0時會發生這種情況。
<0 由于一個錯誤Oracle不能執行SQL語句。當這個錯誤發生時,多數情況下當前的事務應該回滾(rollback)。
?
sqlerrm 這個內嵌結構包含兩個域:?
  • sqlerrml - 在 sqlerrmc 中保存的信息文本的長。
  • sqlerrmc - 最大到70個字符(character)的信息文本,與sqlcode中存儲的錯誤碼相似。
    ?
sqlerrp 保留
sqlerrd 這個二進制整形數組包含6個元素:?
  • sqlerrd[0] - 保留
  • sqlerrd[1] - 保留
  • sqlerrd[2] - 最近被執行的SQL語句影響的記錄行數。
  • sqlerrd[3] - 保留
  • sqlerrd[4] - 分析最近執行語句出現錯誤,錯誤的開始字符的偏移。
  • sqlerrd[5] - 保留
    ?
sqlwarn 這個單字符數組包含8個元素,用于警告標志。Oracle使用字符' W '設置一個標記。?
?
sqlwarn[0] 當其它標記被設置時設置。
sqlwarn[1] 當輸出宿主變量中保存的是被截斷的字段值時設置。
sqlwarn[2] 當計算SQL統計如AVG或SUM時,如果一個 NULL 字段不能被使用時被設置。
sqlwarn[3] SELECT 的字段數與 INTO 句中指定的宿主變量數不等時設置。
sqlwarn[4] 當沒有使用 WHERE 子句的 UPDATE DELETE 語句處理了數據表中的每一行時設置。
sqlwarn[5] 當由于PL/SQL編譯錯誤而導致procedure/function/package/package body創建命令失敗時設置。
sqlwarn[6] 不再使用
sqlwarn[7] 不再使用
?
sqlext 保留

SQLCA只能在 sqlerrm 域中存儲最大70字符長的錯誤信息。要得到更長的(或嵌套的)全部錯誤信息字符,可以使用 sqlglm() 函數:

      void sqlglm(char *msg_buf, size_t *buf_size, size_t *msg_length);
    

msg_buf 是Oracle存儲錯誤信息的字符緩沖; buf_size 指定 msg_buf 的長(byte); Oracle在 *msg_length 中存放實際的錯誤信息長。Oracle錯誤信息的最大長度是512字節(byte)。


WHENEVER語句

這個表達式進行自動錯誤檢查和處理。語法是:

      EXEC SQL WHENEVER <condition> <action>;
    

Oracle自動檢查 <condition> 的SQLCA,當條件被檢測到時,程序會自動執行 <action>

<condition> 可以是下列各項:

  • SQLWARNING - 由于Oracle返回一個警告而設置 sqlwarn[0]
  • SQLERROR - 由于Oracle返回一個錯誤, sqlcode 的值為負
  • NOT FOUND - 由于Oracle按 WHERE 的條件沒有找到任何一條記錄,或 SELECT INTO FETCH 返回 0 條記錄,而使 sqlcode 為正

<action> 可以為下列各項:

  • CONTINUE - 只要可能,程序會嘗試繼續運行之后的語句
  • DO - 程序將控制權交給一個錯誤處理模塊
  • GOTO <label> - 程序跳轉到被標示的語句
  • STOP - 調用 exit() 結束程序,回滾未提交的操作

下面是 WHENEVER 語句的例子:

      EXEC SQL WHENEVER SQLWARNING DO print_warning_msg();

EXEC SQL WHENEVER NOT FOUND GOTO handle_empty;
    

下面是一個更詳細的例子:

      /* code to find student name given id */

/* ... */

for (;;)

{

    printf("Give student id number : ");

    scanf("%d", &id);

    EXEC SQL WHENEVER NOT FOUND GOTO notfound;

    EXEC SQL SELECT studentname INTO :st_name

             FROM   student

             WHERE  studentid = :id;

    printf("Name of student is %s.\n", st_name);

    continue;

notfound:

    printf("No record exists for id %d!\n", id);

}

/* ... */
    

注意 WHENEVER 表達式不遵從標準C的作用域規則,整個程序都是它的作用域。舉例來說,如果下面的語句在你程序中的什么地方(如在一個循環之前):

      EXEC SQL WHENEVER NOT FOUND DO break; 
    

這個文件在這行之后出現的所有SQL語句都會受其影響。當不再需要 WHENEVER 的來影響程序時(比如在你的循環之后),確保使用下面的語句取消它的使用。

      EXEC SQL WHENEVER NOT FOUND CONTINUE; 
    


Demo程序

注意: 例程會創建、使用四個表: DEPT EMP PAY1 PAY2 。注意你的數據庫中可能會存在相同名子的表!

在leland系統的 /afs/ir/class/cs145/code/proc 下有很多例程。它們被命名為 sample*.pc (C用戶)和 cppdemo*.pc (C++用戶)。 " .pc "是Pro*C代碼的擴展名。由于有幾個固定的步驟復制這些文件,所以不要手工復制它們。下面介紹如何下載和設置例程:

  1. 確認你已經運行了 source /afs/ir/class/cs145/all.env
  2. 在你的目錄下,運行 load_samples <db_username> <db_passwd> <sample_dir> <sample_idr> 是你想放置例程的地方(例: load_samples sally etaoinshrdlu cs145_samples
  3. cd <sample_dir>
  4. 運行 make samples (C++用戶運行 make cppsamples )編譯所有的例程。

第2步將會建立樣品數據庫,創建在 <sample_dir> 指定的新目錄,然后把文件復制到目錄下。它會在例程中修改你的用戶名和密碼,所有你不必每次運行例程時都要輸入用戶名和密碼。 sample1 cppdemo1 也為用戶提供了輸入用戶名和密碼的接口,當你想要學習如何使用的時候。如果在第2步輸入用戶名和密碼時產生了任何錯誤,只要在你的目錄下運行 clean_sample <db_username> <db_passwd> & lt;sample_dir> ,然后重做第2步到第4步。

對于第4步,你可以單獨編譯每一個例程。比如單獨編譯 sample1.pc 。編譯過程實際上有兩句:

  1. proc iname=sample1.pc
    把內嵌SQL代碼轉換為相應的庫調用,輸出 sample1.c
  2. cc <a_number_of_flags_here> sample1.c
    主成可執行的 sample1

編譯你自己的代碼,如 foo.pc ,只要修改 Makefile 的幾個變量:在 SAMPLES 變量中加入 foo 程序名,在 SAMPLE_SRC 變量中加入 foo.pc 源文件名。然后,寫好 foo.pc make foo 。 foo.pc 被預編譯為 foo.c ,再編譯為可執行的 foo 。C++用戶要把程序名和源文件名加入到 CPPSAMPLE CPPSAMPLE_SRC 而不是 SAMPLES SAMPLE_SRC 。

例程運行于下面的數據庫表上:

      CREATE TABLE DEPT

    (DEPTNO    NUMBER(2) NOT NULL,

     DNAME     VARCHAR2(14),

     LOC       VARCHAR2(13));



CREATE TABLE EMP

    (EMPNO     NUMBER(4) NOT NULL,

     ENAME     VARCHAR2(10),

     JOB       VARCHAR2(9),

     MGR       NUMBER(4),

     HIREDATE  DATE,

     SAL       NUMBER(7, 2),

     COMM      NUMBER(7, 2),

     DEPTNO    NUMBER(2));



CREATE TABLE PAY1

    (ENAME     VARCHAR2(10),

     SAL       NUMBER(7, 2));



CREATE TABLE PAY2

    (ENAME     VARCHAR2(10),

     SAL       NUMBER(7, 2));
    

當在第2步運行 load_samples 的時候這些表就自動創建好了。一些tuples(譯注:沒找到合適的詞,把原詞放這了)也插入了。你可以在程序運行前查看這些表也可以任意操作這些表(如:插入、刪除或是更新tuples)。當你運行 clean_sample 時,這些表自動被刪除。注意: clean_sample 也會清理整個 <sample_dir>;

確保在運行這個命令之前將你自己的文件轉移到其它的地方!

你應該在運行它之前看一看源代碼,頭部的注釋描述了程序都做些什么。例如, sample1 從一個雇員的 EMPNO 得到他的名子和工資,從 EMP 表中得到的那個雇員的傭金。

通過學習源程序你應該可以學到下面的東西:

  • 如何從主機連接到Oracle
  • 如何在C/C++中嵌入SQL
  • 如何使用游標
  • 如何使用宿主變量與數據庫通訊
  • 如何使用 WHENEVER 進行不同的錯誤處理動作
  • 如何使用指示器變量檢測輸出中的空值

現在,你可以使用這些技術編寫你自己的數據庫應用程序了。And have fun!


C++用戶

要使用預編譯器生成合適的C++代碼,你需要注意下面的事項:

  • 代碼被預編譯器展開。要得到C++代碼,你需要在執行 proc 時設置 CODE=CPP 選項。
  • 解析能力。 proc PARSE 選項可以是下面的值:
    • PARSE=NONE . C預處理指令只能在聲明節中被解析,所以所有的宿主變量需要在聲明節中聲明。
    • PARSE=PARTIAL . C預處理指令能被解析;然而,所有的宿主變量需要在聲明節中聲明。
    • PARSE=FULL . C預處理指令能被解析,而且宿主變量可以聲明在任何地方。當 CODE 不為 CPP 時,這是默認設置;但當 CODE=CPP 時,指定 PARSE=FULL 卻是個錯誤。
    所以,C++用戶必須指定 PARSE=NONE PARSE=PARTIAL ,因此這也失去了在任意地方聲明宿主變量的自由。更有,宿主變量必須被包在一個聲明節中,如下:
            EXEC SQL BEGIN DECLARE SECTION;
    
        // declarations...
    
    EXEC SQL END DECLARE SECTION;
          
    你需要使用這種方法去聲明所有的宿主和指示器變量。
  • 文件擴展名。你要指定設置 CPP_SUFFIX=cc CPP_SUFFIX=C 。
  • 頭文件定位。默認情況下, proc 像標準定位 stdio.h 文件一樣查找頭文件。然而C++有自己的頭文件,如 iostream.h ,被放置在別處。所以,你需要使用 SYS_INCLUDE 選項指定 proc 查找頭文件的路徑。

Pro*C支持的嵌入SQL語句列表

聲明表達式
EXEC SQL ARRAYLEN 在PL/SQL中使用宿主變量
EXEC SQL BEGIN DECLARE SECTION
EXEC SQL END DECLARE SECTION
聲明宿主變量
EXEC SQL DECLARE 給Oracle對像命名
EXEC SQL INCLUDE 從文件中復制
EXEC SQL TYPE 同等化數據類型
EXEC SQL VAR 同等化變量
EXEC SQL WHENEVER 處理運行時錯誤
執行表達式
EXEC SQL ALLOCATE 聲明、控制Oracle數據
EXEC SQL ALTER
EXEC SQL ANALYZE
EXEC SQL AUDIT
EXEC SQL COMMENT
EXEC SQL CONNECT
EXEC SQL CREATE
EXEC SQL DROP
EXEC SQL GRANT
EXEC SQL NOAUDIT
EXEC SQL RENAME
EXEC SQL REVOKE
EXEC SQL TRUNCATE
EXEC SQL CLOSE
EXEC SQL DELETE 排序、修改Oracle數據
EXEC SQL EXPLAIN PLAN
EXEC SQL FETCH
EXEC SQL INSERT
EXEC SQL LOCK TABLE
EXEC SQL OPEN
EXEC SQL SELECT
EXEC SQL UPDATE
EXEC SQL COMMIT 處理事務
EXEC SQL ROLLBACK
EXEC SQL SAVEPOINT
EXEC SQL SET TRANSACTION
EXEC SQL DESCRIBE 使用動態SQL
EXEC SQL EXECUTE
EXEC SQL PREPARE
EXEC SQL ALTER SESSION 控制會話
EXEC SQL SET ROLE
EXEC SQL EXECUTE
END-EXEC
內嵌PL/SQL塊


This document was written originally by Ankur Jain and Jeff Ullman for CS145, Autumn 1997; revised by Jun Yang for Prof. Jennifer Widom's CS145 class in Spring, 1998; further revisions by Roy Goldman for Prof. Jeff Ullman's CS145 class in Autumn, 1999; further revisions by Calvin Yang for Prof. Jennifer Widom's CS145 class in Spring, 2002.

Pro*C介紹-內嵌SQL


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。?!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 伊人国产在线 | 亚洲精品国产字幕久久vr | 国产成人精品亚洲日本在线 | 亚洲一区中文字幕在线 | 日韩毛片在线影视 | 亚洲精品丝袜在线一区波多野结衣 | 奇米视频888 | 免费国产97久久青草 | 亚洲精品一区二区三区 | 成年女人免费视频 | 国产亚洲精品久久久久久午夜 | 成人综合久久精品色婷婷 | 999热成人精品国产免 | 天天爽天天干天天操 | porno日本xxxx | 最新国产精品自拍 | 蜜桃综合| 亚洲国产另类久久久精品小说 | 成人人免费夜夜视频观看 | 中国男女全黄大片一级 | 国产免费专区 | 四虎影视入口 | 四虎影院在线免费播放 | 中国精品久久精品三级 | 精品视频香蕉尹人在线 | 91视频免费观看网站 | 色综合久久天天综线观看 | 国产探花视频在线观看 | 伊人影院中文字幕 | 91啦视频在线观看 | 国产a级高清版毛片 | 中文字幕日本在线 | 天天综合网天天做天天受 | 久久精品视频免费看 | 亚洲欧美日韩综合一区久久 | 中国美女一级a毛片录像在线 | 青青青激情视频在线最新 | 亚洲视频一区在线播放 | 国语自产偷拍精品视频偷最新 | 成人a毛片手机免费播放 | 久草在线免费资源 |